Browse Source

cli json output, dx bundle fix, `dx serve --platform android`, race condition, drop ssg platform (#3186)

* clean up logging to avoid random extra trace

* fix race condition with updates

* properly wire up verbose

* fix bundling (macos) and logging

* Add structured output

* clean up clap names

* extract out wire format

* switch structured key to json

* fix random println, fallback to `dioxus/platform`

* clean up logging for run/build

* clean up logging around project

* remove manual exits

* fix tokio runtime for mobile

* rework dog app

* rip out ssg

* Switch dioxus/axum to dioxus/server

* add android template inline

* pre restructure for bundle prep

* add the whole res directory

* Better theme for the app

* remove mobile demo now that most apps work natively

* self-referential android

* only use deep linking for assets

* fix imports for android

* clippy, fixup ios and android

* I'm not boxing compiler message you can't make me

* fix clippy on unix
Jonathan Kelley 7 months ago
parent
commit
ac3e33af46
100 changed files with 2230 additions and 5541 deletions
  1. 4 0
      .gitignore
  2. 54 116
      Cargo.lock
  3. 7 13
      Cargo.toml
  4. 1 1
      example-projects/fullstack-hackernews/Cargo.toml
  5. 1 3
      examples/clock.rs
  6. 9 11
      examples/dog_app.rs
  7. 1 1
      examples/fullstack-router/Cargo.toml
  8. 1 1
      examples/fullstack-streaming/Cargo.toml
  9. 0 10
      examples/mobile_demo/.gitignore
  10. 0 4007
      examples/mobile_demo/Cargo.lock
  11. 0 51
      examples/mobile_demo/Cargo.toml
  12. 0 11
      examples/mobile_demo/README.md
  13. 0 8
      examples/mobile_demo/mobile.toml
  14. 0 12
      examples/mobile_demo/src/index.html
  15. 0 90
      examples/mobile_demo/src/lib.rs
  16. 0 4
      examples/ssg-github-pages/.gitignore
  17. 0 17
      examples/ssg-github-pages/Cargo.toml
  18. 0 70
      examples/ssg-github-pages/src/main.rs
  19. 0 4
      examples/ssg-router/.gitignore
  20. 0 16
      examples/ssg-router/Cargo.toml
  21. 0 54
      examples/ssg-router/src/main.rs
  22. 0 4
      examples/ssg-simple/.gitignore
  23. 0 16
      examples/ssg-simple/Cargo.toml
  24. 0 17
      examples/ssg-simple/src/main.rs
  25. 5 0
      packages/cli-config/src/lib.rs
  26. 4 6
      packages/cli/Cargo.toml
  27. 0 1
      packages/cli/assets/android/.gitignore
  28. 3 0
      packages/cli/assets/android/MainActivity.kt
  29. 15 0
      packages/cli/assets/android/gen/.gitignore
  30. 49 0
      packages/cli/assets/android/gen/app/build.gradle.kts
  31. 21 0
      packages/cli/assets/android/gen/app/proguard-rules.pro
  32. 18 0
      packages/cli/assets/android/gen/app/src/main/AndroidManifest.xml
  33. 0 0
      packages/cli/assets/android/gen/app/src/main/assets/.gitignore
  34. 0 0
      packages/cli/assets/android/gen/app/src/main/kotlin/.gitignore
  35. 30 0
      packages/cli/assets/android/gen/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  36. 170 0
      packages/cli/assets/android/gen/app/src/main/res/drawable/ic_launcher_background.xml
  37. 5 0
      packages/cli/assets/android/gen/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  38. BIN
      packages/cli/assets/android/gen/app/src/main/res/mipmap-hdpi/ic_launcher.webp
  39. BIN
      packages/cli/assets/android/gen/app/src/main/res/mipmap-mdpi/ic_launcher.webp
  40. BIN
      packages/cli/assets/android/gen/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  41. BIN
      packages/cli/assets/android/gen/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  42. BIN
      packages/cli/assets/android/gen/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  43. 6 0
      packages/cli/assets/android/gen/app/src/main/res/values/colors.xml
  44. 3 0
      packages/cli/assets/android/gen/app/src/main/res/values/strings.xml
  45. 7 0
      packages/cli/assets/android/gen/app/src/main/res/values/styles.xml
  46. 22 0
      packages/cli/assets/android/gen/build.gradle.kts
  47. 25 0
      packages/cli/assets/android/gen/gradle.properties
  48. BIN
      packages/cli/assets/android/gen/gradle/wrapper/gradle-wrapper.jar
  49. 6 0
      packages/cli/assets/android/gen/gradle/wrapper/gradle-wrapper.properties
  50. 185 0
      packages/cli/assets/android/gen/gradlew
  51. 90 0
      packages/cli/assets/android/gen/gradlew.bat
  52. 2 0
      packages/cli/assets/android/gen/settings.gradle
  53. 10 6
      packages/cli/assets/ios/ios.plist.hbs
  54. 8 8
      packages/cli/assets/macos/mac.plist.hbs
  55. 101 77
      packages/cli/src/build/builder.rs
  56. 114 139
      packages/cli/src/build/bundle.rs
  57. 5 4
      packages/cli/src/build/mod.rs
  58. 9 46
      packages/cli/src/build/progress.rs
  59. 377 28
      packages/cli/src/build/request.rs
  60. 7 0
      packages/cli/src/build/templates.rs
  61. 12 12
      packages/cli/src/build/verify.rs
  62. 0 0
      packages/cli/src/build/web.rs
  63. 1 6
      packages/cli/src/bundle_utils.rs
  64. 14 23
      packages/cli/src/cli/autoformat.rs
  65. 10 30
      packages/cli/src/cli/build.rs
  66. 162 75
      packages/cli/src/cli/bundle.rs
  67. 23 25
      packages/cli/src/cli/check.rs
  68. 7 6
      packages/cli/src/cli/clean.rs
  69. 6 6
      packages/cli/src/cli/config.rs
  70. 4 3
      packages/cli/src/cli/create.rs
  71. 3 2
      packages/cli/src/cli/doctor.rs
  72. 7 6
      packages/cli/src/cli/init.rs
  73. 3 6
      packages/cli/src/cli/link.rs
  74. 16 11
      packages/cli/src/cli/mod.rs
  75. 12 8
      packages/cli/src/cli/run.rs
  76. 17 7
      packages/cli/src/cli/serve.rs
  77. 12 10
      packages/cli/src/cli/translate.rs
  78. 16 0
      packages/cli/src/cli/verbosity.rs
  79. 29 9
      packages/cli/src/config/bundle.rs
  80. 63 54
      packages/cli/src/dioxus_crate.rs
  81. 9 0
      packages/cli/src/error.rs
  82. 125 95
      packages/cli/src/logging.rs
  83. 39 39
      packages/cli/src/main.rs
  84. 1 4
      packages/cli/src/platform.rs
  85. 38 8
      packages/cli/src/serve/handle.rs
  86. 17 16
      packages/cli/src/serve/mod.rs
  87. 95 112
      packages/cli/src/serve/output.rs
  88. 14 3
      packages/cli/src/serve/runner.rs
  89. 15 7
      packages/cli/src/serve/server.rs
  90. 1 0
      packages/cli/src/slog.rs
  91. 0 1
      packages/config-macro/Cargo.toml
  92. 0 1
      packages/config-macro/src/lib.rs
  93. 3 0
      packages/desktop/.vscode/settings.json
  94. 6 1
      packages/desktop/Cargo.toml
  95. 4 2
      packages/desktop/src/launch.rs
  96. 50 0
      packages/desktop/src/protocol.rs
  97. 4 17
      packages/devtools/src/lib.rs
  98. 2 7
      packages/dioxus/Cargo.toml
  99. 14 60
      packages/dioxus/src/launch.rs
  100. 1 17
      packages/dioxus/src/lib.rs

+ 4 - 0
.gitignore

@@ -24,3 +24,7 @@ node_modules/
 /packages/playwright-report/
 /packages/playwright/.cache/
 
+
+# ignore the output of tmps
+tmp/
+bundle/

+ 54 - 116
Cargo.lock

@@ -432,7 +432,7 @@ dependencies = [
  "base64ct",
  "blake2",
  "cpufeatures",
- "password-hash 0.5.0",
+ "password-hash",
 ]
 
 [[package]]
@@ -558,8 +558,8 @@ dependencies = [
  "memchr",
  "pin-project-lite",
  "tokio",
- "zstd 0.13.2",
- "zstd-safe 7.2.1",
+ "zstd",
+ "zstd-safe",
 ]
 
 [[package]]
@@ -2466,12 +2466,6 @@ version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "013b6c2c3a14d678f38cd23994b02da3a1a1b6a5d1eedddfe63a5a5f11b13a81"
 
-[[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"
@@ -3152,7 +3146,6 @@ dependencies = [
 name = "dioxus"
 version = "0.6.0-alpha.4"
 dependencies = [
- "axum 0.7.7",
  "criterion",
  "dioxus",
  "dioxus-config-macro",
@@ -3170,7 +3163,6 @@ dependencies = [
  "dioxus-router",
  "dioxus-signals",
  "dioxus-ssr",
- "dioxus-static-site-generation",
  "dioxus-web",
  "env_logger 0.10.2",
  "futures-util",
@@ -3235,6 +3227,7 @@ dependencies = [
  "dioxus-core",
  "dioxus-core-types",
  "dioxus-devtools-types",
+ "dioxus-dx-wire-format",
  "dioxus-fullstack",
  "dioxus-html",
  "dioxus-rsx",
@@ -3242,8 +3235,6 @@ dependencies = [
  "dioxus-rsx-rosetta",
  "dirs",
  "env_logger 0.11.5",
- "flate2",
- "fs_extra",
  "futures-channel",
  "futures-util",
  "handlebars",
@@ -3253,6 +3244,7 @@ dependencies = [
  "hyper-rustls 0.27.3",
  "hyper-util",
  "ignore",
+ "include_dir",
  "itertools 0.13.0",
  "krates",
  "log",
@@ -3272,7 +3264,6 @@ dependencies = [
  "serde_json",
  "strum 0.26.3",
  "syn 2.0.87",
- "tar",
  "tauri-bundler",
  "tauri-utils",
  "tempfile",
@@ -3294,7 +3285,6 @@ dependencies = [
  "wasm-bindgen-cli-support",
  "wasm-bindgen-shared",
  "wasm-opt",
- "zip 0.6.6",
 ]
 
 [[package]]
@@ -3387,8 +3377,12 @@ dependencies = [
  "global-hotkey",
  "http-range",
  "infer 0.11.0",
+ "jni",
  "lazy-js-bundle",
  "muda 0.11.5",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
  "objc",
  "objc_id",
  "once_cell",
@@ -3453,6 +3447,15 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "dioxus-dx-wire-format"
+version = "0.6.0-alpha.4"
+dependencies = [
+ "cargo_metadata",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "dioxus-examples"
 version = "0.6.0-alpha.4"
@@ -3844,28 +3847,6 @@ dependencies = [
  "rustc-hash 1.1.0",
 ]
 
-[[package]]
-name = "dioxus-static-site-generation"
-version = "0.6.0-alpha.4"
-dependencies = [
- "axum 0.7.7",
- "criterion",
- "dioxus",
- "dioxus-cli-config",
- "dioxus-devtools",
- "dioxus-fullstack",
- "dioxus-isrg",
- "dioxus-lib",
- "dioxus-router",
- "dioxus-ssr",
- "dioxus-web",
- "http 1.1.0",
- "tokio",
- "tower 0.4.13",
- "tower-http",
- "tracing",
-]
-
 [[package]]
 name = "dioxus-tailwind"
 version = "0.0.0"
@@ -5148,15 +5129,6 @@ dependencies = [
  "url",
 ]
 
-[[package]]
-name = "github-pages-static-generation"
-version = "0.1.0"
-dependencies = [
- "dioxus",
- "tower 0.4.13",
- "tracing-subscriber",
-]
-
 [[package]]
 name = "gix-actor"
 version = "0.31.5"
@@ -6321,6 +6293,25 @@ version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
 
+[[package]]
+name = "include_dir"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
+dependencies = [
+ "include_dir_macros",
+]
+
+[[package]]
+name = "include_dir_macros"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
 [[package]]
 name = "indexmap"
 version = "1.9.3"
@@ -8192,17 +8183,6 @@ dependencies = [
  "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 = "password-hash"
 version = "0.5.0"
@@ -8244,18 +8224,6 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
 
-[[package]]
-name = "pbkdf2"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
-dependencies = [
- "digest",
- "hmac",
- "password-hash 0.4.2",
- "sha2",
-]
-
 [[package]]
 name = "pear"
 version = "0.2.9"
@@ -9600,14 +9568,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "router-static-generation"
-version = "0.1.0"
-dependencies = [
- "dioxus",
- "tracing-subscriber",
-]
-
 [[package]]
 name = "rpm"
 version = "0.15.1"
@@ -9635,7 +9595,7 @@ dependencies = [
  "sha2",
  "thiserror",
  "xz2",
- "zstd 0.13.2",
+ "zstd",
 ]
 
 [[package]]
@@ -10435,14 +10395,6 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5dd19be0257552dd56d1bb6946f89f193c6e5b9f13cc9327c4bc84a357507c74"
 
-[[package]]
-name = "simple-static-generation"
-version = "0.1.0"
-dependencies = [
- "dioxus",
- "tracing-subscriber",
-]
-
 [[package]]
 name = "simple_asn1"
 version = "0.6.2"
@@ -11142,9 +11094,9 @@ dependencies = [
 
 [[package]]
 name = "tao"
-version = "0.30.6"
+version = "0.30.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "833b4d43383d76d5078d72f3acd977f47eb5b6751eb40baa665d13828e7b79df"
+checksum = "2a93f2c6b8fdaeb7f417bda89b5bc767999745c3052969664ae1fa65892deb7e"
 dependencies = [
  "bitflags 2.6.0",
  "cocoa 0.26.0",
@@ -11874,6 +11826,16 @@ dependencies = [
  "tracing-core",
 ]
 
+[[package]]
+name = "tracing-serde"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
 [[package]]
 name = "tracing-subscriber"
 version = "0.3.18"
@@ -11884,12 +11846,15 @@ dependencies = [
  "nu-ansi-term",
  "once_cell",
  "regex",
+ "serde",
+ "serde_json",
  "sharded-slab",
  "smallvec",
  "thread_local",
  "tracing",
  "tracing-core",
  "tracing-log",
+ "tracing-serde",
 ]
 
 [[package]]
@@ -13642,18 +13607,10 @@ 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]]
@@ -13698,32 +13655,13 @@ dependencies = [
  "simd-adler32",
 ]
 
-[[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",
+ "zstd-safe",
 ]
 
 [[package]]

+ 7 - 13
Cargo.toml

@@ -54,18 +54,13 @@ members = [
     "packages/server-macro",
     "packages/signals",
     "packages/ssr",
-    "packages/static-generation",
     "packages/lazy-js-bundle",
     "packages/cli-config",
     "packages/devtools",
     "packages/devtools-types",
     "packages/isrg",
     "packages/rsx-hotreload",
-
-    # Static generation examples
-    # "packages/static-generation/examples/simple",
-    # "packages/static-generation/examples/router",
-    # "packages/static-generation/examples/github-pages",
+    "packages/dx-wire-format",
 
     # Playwright tests
     "packages/playwright-tests/liveview",
@@ -94,9 +89,6 @@ members = [
     "examples/fullstack-streaming",
     "examples/fullstack-desktop",
     "examples/fullstack-auth",
-    "examples/ssg-simple",
-    "examples/ssg-router",
-    "examples/ssg-github-pages",
 
     # Playwright tests
     "packages/playwright-tests/liveview",
@@ -142,8 +134,9 @@ generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.
 dioxus-devtools = { path = "packages/devtools", version = "0.6.0-alpha.4" }
 dioxus-devtools-types = { path = "packages/devtools-types", version = "0.6.0-alpha.4" }
 dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.4" }
-dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.4" }
 dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.4", default-features = false }
+dioxus-dx-wire-format = { path = "packages/dx-wire-format", version = "0.6.0-alpha.4" }
+
 lazy-js-bundle = { path = "packages/lazy-js-bundle", version = "0.6.0-alpha.4" }
 manganis = { path = "packages/manganis/manganis", version = "0.6.0-alpha.4" }
 manganis-core = { path = "packages/manganis/manganis-core", version = "0.6.0-alpha.4" }
@@ -222,10 +215,11 @@ dirs = "5.0.1"
 cargo-config2 = "0.1.26"
 criterion = { version = "0.5" }
 walrus = "*"
+cargo_metadata = "0.18.1"
 
 # desktop
 wry = { version = "0.45.0", default-features = false }
-tao = { version = "0.30.0", features = ["rwh_05"] }
+tao = { version = "=0.30.0", features = ["rwh_05"] }
 webbrowser = "1.0.1"
 infer = "0.16.0"
 dunce = "1.0.2"
@@ -310,8 +304,8 @@ default = ["desktop"]
 desktop = ["dioxus/desktop"]
 liveview = ["dioxus/liveview"]
 fullstack = ["dioxus/fullstack"]
-axum = ["dioxus/axum"]
-server = ["dioxus/axum"]
+server = ["dioxus/server"]
+mobile = ["dioxus/mobile"]
 web = ["dioxus/web"]
 http = ["dep:reqwest", "dep:http-range"]
 

+ 1 - 1
example-projects/fullstack-hackernews/Cargo.toml

@@ -18,5 +18,5 @@ tracing-subscriber = "0.3.17"
 
 [features]
 default = []
-server = ["dioxus/axum"]
+server = ["dioxus/server"]
 web = ["dioxus/web"]

+ 1 - 3
examples/clock.rs

@@ -5,8 +5,6 @@ use async_std::task::sleep;
 use dioxus::prelude::*;
 use web_time::Instant;
 
-const STYLE: Asset = asset!("/examples/assets/clock.css");
-
 fn main() {
     dioxus::launch(app);
 }
@@ -36,7 +34,7 @@ fn app() -> Element {
     );
 
     rsx! {
-        document::Link { rel: "stylesheet", href: STYLE }
+        document::Stylesheet { href: asset!("/examples/assets/clock.css") }
         div { id: "app",
             div { id: "title", "Carpe diem 🎉" }
             div { id: "clock-display", "{time}" }

+ 9 - 11
examples/dog_app.rs

@@ -39,10 +39,8 @@ fn app() -> Element {
 
         rsx! {
             for cur_breed in breeds.message.keys().take(20).cloned() {
-                li { key: "{cur_breed}",
-                    button { onclick: move |_| breed.set(cur_breed.clone()),
-                        "{cur_breed}"
-                    }
+                button { onclick: move |_| breed.set(cur_breed.clone()),
+                    "{cur_breed}"
                 }
             }
         }
@@ -55,11 +53,9 @@ fn app() -> Element {
     };
 
     rsx! {
-        h1 { "Select a dog breed!" }
-        div { height: "500px", display: "flex",
-            ul { width: "100px", {breed_list} }
-            div { flex: 1, BreedPic { breed } }
-        }
+        h1 { "Select a dog breed: {breed}" }
+        BreedPic { breed }
+        div { width: "400px", {breed_list} }
     }
 }
 
@@ -81,8 +77,10 @@ fn BreedPic(breed: Signal<String>) -> Element {
 
     match fut.read_unchecked().as_ref() {
         Some(Ok(resp)) => rsx! {
-            button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
-            img { max_width: "500px", max_height: "500px", src: "{resp.message}" }
+            div {
+                button { onclick: move |_| fut.restart(), padding: "5px", background_color: "gray", color: "white", border_radius: "5px", "Click to fetch another doggo" }
+                img { max_width: "500px", max_height: "500px", src: "{resp.message}" }
+            }
         },
         Some(Err(_)) => rsx! { "loading image failed" },
         None => rsx! { "loading image..." },

+ 1 - 1
examples/fullstack-router/Cargo.toml

@@ -14,6 +14,6 @@ serde = { version = "1.0.159", features = ["derive"] }
 
 [features]
 default = []
-server = ["axum", "dioxus/axum"]
+server = ["axum", "dioxus/server"]
 web = ["dioxus/web"]
 

+ 1 - 1
examples/fullstack-streaming/Cargo.toml

@@ -20,5 +20,5 @@ once_cell = "1.19.0"
 
 [features]
 default = []
-server = ["dioxus/axum", "dep:tokio"]
+server = ["dioxus/server", "dep:tokio"]
 web = ["dioxus/web"]

+ 0 - 10
examples/mobile_demo/.gitignore

@@ -1,10 +0,0 @@
-# Rust
-target/
-**/*.rs.bk
-
-# cargo-mobile2
-.cargo/
-/gen
-
-# macOS
-.DS_Store

+ 0 - 4007
examples/mobile_demo/Cargo.lock

@@ -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",
-]

+ 0 - 51
examples/mobile_demo/Cargo.toml

@@ -1,51 +0,0 @@
-[package]
-name = "mobile-demo"
-version = "0.1.0"
-authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
-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 = "<android-project-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"

+ 0 - 11
examples/mobile_demo/README.md

@@ -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.

+ 0 - 8
examples/mobile_demo/mobile.toml

@@ -1,8 +0,0 @@
-[app]
-name = "mobile-demo"
-stylized-name = "Mobile Demo"
-domain = "example.com"
-template-pack = "wry"
-
-[apple]
-development-team = "34U4FG9TJ8"

+ 0 - 12
examples/mobile_demo/src/index.html

@@ -1,12 +0,0 @@
-<!DOCTYPE html>
- <html>
-   <head>
-     <title>Dioxus app</title>
-     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-     <!-- CUSTOM HEAD -->
-   </head>
-   <body>
-     <div id="main"></div>
-     <!-- MODULE LOADER -->
-   </body>
- </html>

+ 0 - 90
examples/mobile_demo/src/lib.rs

@@ -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<F: FnOnce() -> 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
-    dioxus::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}" }
-                }
-            }
-        }
-    }
-}

+ 0 - 4
examples/ssg-github-pages/.gitignore

@@ -1,4 +0,0 @@
-dist
-target
-docs
-.dioxus

+ 0 - 17
examples/ssg-github-pages/Cargo.toml

@@ -1,17 +0,0 @@
-[package]
-name = "github-pages-static-generation"
-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 = ["static-generation", "router"] }
-tower = { workspace = true, features = ["util"] }
-tracing-subscriber = "0.3.18"
-
-[features]
-default = []
-server = ["dioxus/axum"]
-web = ["dioxus/web"]

+ 0 - 70
examples/ssg-github-pages/src/main.rs

@@ -1,70 +0,0 @@
-//! You can use the `github_pages` method to set up a preset for github pages.
-//! This will output your files in the `/docs` directory and set up a `404.html` file.
-
-#![allow(unused)]
-use dioxus::prelude::*;
-
-// Generate all routes and output them to the static path
-fn main() {
-    dioxus::LaunchBuilder::new()
-        .with_cfg(dioxus::static_site_generation::Config::new().github_pages())
-        .launch(|| {
-            rsx! {
-                Router::<Route> {}
-            }
-        });
-}
-
-#[derive(Clone, Routable, Debug, PartialEq)]
-enum Route {
-    #[route("/")]
-    Home {},
-
-    #[route("/blog")]
-    Blog,
-
-    // You must include a catch all route to handle 404s
-    #[route("/:..route")]
-    PageNotFound { route: Vec<String> },
-}
-
-#[component]
-fn Blog() -> Element {
-    rsx! {
-        Link { to: Route::Home {}, "Go to counter" }
-        table {
-            tbody {
-                for _ in 0..100 {
-                    tr {
-                        for _ in 0..100 {
-                            td { "hello world!" }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-#[component]
-fn Home() -> Element {
-    let mut count = use_signal(|| 0);
-
-    rsx! {
-        Link { to: Route::Blog {}, "Go to blog" }
-        div {
-            h1 { "High-Five counter: {count}" }
-            button { onclick: move |_| count += 1, "Up high!" }
-            button { onclick: move |_| count -= 1, "Down low!" }
-        }
-    }
-}
-
-#[component]
-fn PageNotFound(route: Vec<String>) -> Element {
-    rsx! {
-        h1 { "Page not found" }
-        p { "We are terribly sorry, but the page you requested doesn't exist." }
-        pre { color: "red", "log:\nattempted to navigate to: {route:?}" }
-    }
-}

+ 0 - 4
examples/ssg-router/.gitignore

@@ -1,4 +0,0 @@
-dist
-target
-static
-.dioxus

+ 0 - 16
examples/ssg-router/Cargo.toml

@@ -1,16 +0,0 @@
-[package]
-name = "router-static-generation"
-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 = ["static-generation", "router"] }
-tracing-subscriber = "0.3.18"
-
-[features]
-default = []
-server = ["dioxus/axum"]
-web = ["dioxus/web"]

+ 0 - 54
examples/ssg-router/src/main.rs

@@ -1,54 +0,0 @@
-//! Static generation works out of the box with the router. Just add a router anywhere in your app and it will generate any static routes for you!
-
-#![allow(unused)]
-use dioxus::prelude::*;
-
-// Generate all routes and output them to the static path
-fn main() {
-    dioxus::launch(|| {
-        rsx! {
-            Router::<Route> {}
-        }
-    });
-}
-
-#[derive(Clone, Routable, Debug, PartialEq)]
-enum Route {
-    #[route("/")]
-    Home {},
-
-    #[route("/blog")]
-    Blog,
-}
-
-#[component]
-fn Blog() -> Element {
-    rsx! {
-        Link { to: Route::Home {}, "Go to counter" }
-        table {
-            tbody {
-                for _ in 0..100 {
-                    tr {
-                        for _ in 0..100 {
-                            td { "hello!" }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-#[component]
-fn Home() -> Element {
-    let mut count = use_signal(|| 0);
-
-    rsx! {
-        Link { to: Route::Blog {}, "Go to blog" }
-        div {
-            h1 { "High-Five counter: {count}" }
-            button { onclick: move |_| count += 1, "Up high!" }
-            button { onclick: move |_| count -= 1, "Down low!" }
-        }
-    }
-}

+ 0 - 4
examples/ssg-simple/.gitignore

@@ -1,4 +0,0 @@
-dist
-target
-static
-.dioxus

+ 0 - 16
examples/ssg-simple/Cargo.toml

@@ -1,16 +0,0 @@
-[package]
-name = "simple-static-generation"
-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 = ["static-generation"] }
-tracing-subscriber = "0.3.18"
-
-[features]
-default = []
-server = ["dioxus/axum"]
-web = ["dioxus/web"]

+ 0 - 17
examples/ssg-simple/src/main.rs

@@ -1,17 +0,0 @@
-//! Static generation lets you pre-render your entire app to static files and then hydrate it on the client.
-use dioxus::prelude::*;
-
-// Generate all routes and output them to the static path
-fn main() {
-    dioxus::launch(app);
-}
-
-fn app() -> Element {
-    let mut count = use_signal(|| 0);
-
-    rsx! {
-        h1 { "High-Five counter: {count}" }
-        button { onclick: move |_| count += 1, "Up high!" }
-        button { onclick: move |_| count -= 1, "Down low!" }
-    }
-}

+ 5 - 0
packages/cli-config/src/lib.rs

@@ -16,6 +16,11 @@ pub const OUT_DIR: &str = "DIOXUS_OUT_DIR";
 ///
 /// This is not a websocket! There's no protocol!
 pub fn devserver_raw_addr() -> Option<SocketAddr> {
+    // On android, 10.0.2.2 is the default loopback
+    if cfg!(target_os = "android") {
+        return Some("10.0.2.2:8080".parse().unwrap());
+    }
+
     std::env::var(DEVSERVER_RAW_ADDR_ENV)
         .map(|s| s.parse().ok())
         .ok()

+ 4 - 6
packages/cli/Cargo.toml

@@ -21,6 +21,7 @@ dioxus-core-types = { workspace = true }
 dioxus-devtools-types = { workspace = true }
 dioxus-cli-config = { workspace = true }
 dioxus-fullstack = { workspace = true }
+dioxus-dx-wire-format = { workspace = true }
 
 clap = { workspace = true, features = ["derive", "cargo"] }
 thiserror = { workspace = true }
@@ -30,12 +31,11 @@ uuid = { version = "1.3.0", features = ["v4"] }
 serde = { workspace = true, features = ["derive"] }
 serde_json = { workspace = true }
 toml = { workspace = true }
-fs_extra = "1.2.0"
 cargo_toml = { workspace = true }
 futures-util = { workspace = true, features = ["async-await-macro"] }
 notify = { workspace = true, features = ["serde"] }
 html_parser = { workspace = true }
-cargo_metadata = "0.18.1"
+cargo_metadata = { workspace = true }
 tokio = { workspace = true, features = ["full"] }
 tokio-stream = "0.1.15"
 chrono = "0.4.19"
@@ -71,9 +71,6 @@ reqwest = { workspace = true, features = [
     "trust-dns",
     "blocking",
 ] }
-flate2 = "1.0.22"
-tar = "0.4.38"
-zip = "0.6.2"
 tower = { workspace = true }
 once_cell = "1.19.0"
 
@@ -91,7 +88,7 @@ brotli = "6.0.0"
 ignore = "0.4.22"
 env_logger = { workspace = true }
 
-tracing-subscriber = { version = "0.3.18", features = ["std", "env-filter"] }
+tracing-subscriber = { version = "0.3.18", features = ["std", "env-filter", "json"] }
 console-subscriber = { version = "0.3.0", optional = true }
 tracing = { workspace = true }
 wasm-opt = { version = "0.116.1", optional = true }
@@ -118,6 +115,7 @@ strum = { version = "0.26.3", features = ["derive"] }
 
 tauri-utils = { workspace = true }
 tauri-bundler = { workspace = true }
+include_dir = "0.7.4"
 
 [build-dependencies]
 built = { version = "=0.7.4", features = ["git2"] }

+ 0 - 1
packages/cli/assets/android/.gitignore

@@ -1 +0,0 @@
-app-dir/

+ 3 - 0
packages/cli/assets/android/MainActivity.kt

@@ -0,0 +1,3 @@
+package com.example.androidfinal
+
+class MainActivity : WryActivity()

+ 15 - 0
packages/cli/assets/android/gen/.gitignore

@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build
+/captures
+.externalNativeBuild
+jniLibs
+.cxx
+local.properties

+ 49 - 0
packages/cli/assets/android/gen/app/build.gradle.kts

@@ -0,0 +1,49 @@
+plugins {
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    namespace="com.example.androidfinal"
+    compileSdk = 33
+    defaultConfig {
+        applicationId = "com.example.androidfinal"
+        minSdk = 24
+        targetSdk = 33
+        versionCode = 1
+        versionName = "1.0"
+    }
+    buildTypes {
+        getByName("debug") {
+            isDebuggable = true
+            isJniDebuggable = true
+            isMinifyEnabled = false
+            packaging {
+                jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
+                jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
+                jniLibs.keepDebugSymbols.add("*/x86/*.so")
+                jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
+            }
+        }
+        getByName("release") {
+            isMinifyEnabled = true
+             proguardFiles(
+                *fileTree(".") { include("**/*.pro") }
+                    .plus(getDefaultProguardFile("proguard-android-optimize.txt"))
+                    .toList().toTypedArray()
+            )
+        }
+    }
+    kotlinOptions {
+        jvmTarget = "1.8"
+    }
+    buildFeatures {
+        buildConfig = true
+    }
+}
+
+dependencies {
+    implementation("androidx.webkit:webkit:1.6.1")
+    implementation("androidx.appcompat:appcompat:1.6.1")
+    implementation("com.google.android.material:material:1.8.0")
+}

+ 21 - 0
packages/cli/assets/android/gen/app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 18 - 0
packages/cli/assets/android/gen/app/src/main/AndroidManifest.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application android:hasCode="true" android:supportsRtl="true" android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name" android:theme="@style/AppTheme">
+        <activity android:configChanges="orientation|keyboardHidden" android:exported="true"
+            android:label="@string/app_name" android:name="com.example.androidfinal.MainActivity">
+            <meta-data android:name="android.app.lib_name" android:value="androidfinal" />
+            <meta-data android:name="android.app.func_name" android:value="ANativeActivity_onCreate" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 0 - 0
packages/cli/assets/android/gen/app/src/main/assets/.gitignore


+ 0 - 0
packages/cli/assets/android/gen/app/src/main/kotlin/.gitignore


+ 30 - 0
packages/cli/assets/android/gen/app/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="85.84757"
+                android:endY="92.4963"
+                android:startX="42.9492"
+                android:startY="49.59793"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>

+ 170 - 0
packages/cli/assets/android/gen/app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

+ 5 - 0
packages/cli/assets/android/gen/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

BIN
packages/cli/assets/android/gen/app/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
packages/cli/assets/android/gen/app/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
packages/cli/assets/android/gen/app/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
packages/cli/assets/android/gen/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
packages/cli/assets/android/gen/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


+ 6 - 0
packages/cli/assets/android/gen/app/src/main/res/values/colors.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#008577</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#D81B60</color>
+</resources>

+ 3 - 0
packages/cli/assets/android/gen/app/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Androidfinal</string>
+</resources>

+ 7 - 0
packages/cli/assets/android/gen/app/src/main/res/values/styles.xml

@@ -0,0 +1,7 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 22 - 0
packages/cli/assets/android/gen/build.gradle.kts

@@ -0,0 +1,22 @@
+buildscript {
+    repositories {
+        google()
+        mavenCentral()
+    }
+    dependencies {
+        classpath("com.android.tools.build:gradle:8.7.0")
+        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20")
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+tasks.register("clean").configure {
+    delete("build")
+}
+

+ 25 - 0
packages/cli/assets/android/gen/gradle.properties

@@ -0,0 +1,25 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonFinalResIds=false

BIN
packages/cli/assets/android/gen/gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
packages/cli/assets/android/gen/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Tue May 10 19:22:52 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME

+ 185 - 0
packages/cli/assets/android/gen/gradlew

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"

+ 90 - 0
packages/cli/assets/android/gen/gradlew.bat

@@ -0,0 +1,90 @@
+
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 2 - 0
packages/cli/assets/android/gen/settings.gradle

@@ -0,0 +1,2 @@
+include ':app'
+

+ 10 - 6
packages/cli/assets/ios/ios.plist → packages/cli/assets/ios/ios.plist.hbs

@@ -3,14 +3,18 @@
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
-  <key>CFBundleIdentifier</key>
-  <string>com.dioxuslabs</string>
   <key>CFBundleDisplayName</key>
-  <string>DioxusApp</string>
-  <key>CFBundleName</key>
-  <string>DioxusApp</string>
+  <string>{{ display_name }}</string>
+
   <key>CFBundleExecutable</key>
-  <string>DioxusApp</string>
+  <string>{{ executable_name }}</string>
+
+  <key>CFBundleIdentifier</key>
+  <string>{{ bundle_identifier }}</string>
+
+  <key>CFBundleName</key>
+  <string>{{ bundle_name }}</string>
+
   <key>CFBundleVersion</key>
   <string>0.1.0</string>
   <key>CFBundleShortVersionString</key>

+ 8 - 8
packages/cli/assets/macos/mac.plist → packages/cli/assets/macos/mac.plist.hbs

@@ -3,23 +3,23 @@
 <plist version="1.0">
 	<dict>
 		<key>CFBundleDisplayName</key>
-		<string>DioxusApp</string>
+		<string>{{ display_name }}</string>
 
 		<key>CFBundleExecutable</key>
-		<string>DioxusApp</string>
+		<string>{{ executable_name }}</string>
+
+		<key>CFBundleIdentifier</key>
+		<string>{{ bundle_identifier }}</string>
+
+		<key>CFBundleName</key>
+		<string>{{ bundle_name }}</string>
 
 		<key>CFBundleIconFile</key>
 		<string>icon.icns</string>
 
-		<key>CFBundleIdentifier</key>
-		<string>org.dioxuslabs.dioxus-desktop</string>
-
 		<key>CFBundleInfoDictionaryVersion</key>
 		<string>6.0</string>
 
-		<key>CFBundleName</key>
-		<string>App</string>
-
 		<key>CFBundlePackageType</key>
 		<string>APPL</string>
 

+ 101 - 77
packages/cli/src/builder/runner.rs → packages/cli/src/build/builder.rs

@@ -1,6 +1,6 @@
 use crate::{
-    AppBundle, BuildArgs, BuildRequest, BuildStage, BuildUpdate, DioxusCrate, Platform, ProgressRx,
-    ProgressTx, Result,
+    AppBundle, BuildArgs, BuildRequest, BuildStage, BuildUpdate, DioxusCrate, ProgressRx,
+    ProgressTx, Result, StructuredOutput,
 };
 use std::time::{Duration, Instant};
 
@@ -40,6 +40,7 @@ impl Builder {
     pub(crate) fn start(krate: &DioxusCrate, args: BuildArgs) -> Result<Self> {
         let (tx, rx) = futures_channel::mpsc::unbounded();
         let request = BuildRequest::new(krate.clone(), args, tx.clone());
+
         Ok(Self {
             krate: krate.clone(),
             request: request.clone(),
@@ -49,14 +50,7 @@ impl Builder {
                 // We wont bother verifying on subsequent builds
                 request.verify_tooling().await?;
 
-                let res = request.build_all().await;
-
-                // The first launch gets some extra logging :)
-                if res.is_ok() {
-                    tracing::info!("Build completed successfully, launching app! 💫")
-                }
-
-                res
+                request.build_all().await
             }),
             tx,
             rx,
@@ -91,79 +85,76 @@ impl Builder {
             },
         };
 
-        tracing::trace!("Build update: {update:?}");
-
         // Update the internal stage of the build so the UI can render it
         match &update {
             BuildUpdate::Progress { stage } => {
                 // Prevent updates from flowing in after the build has already finished
                 if !self.is_finished() {
                     self.stage = stage.clone();
-                }
 
-                match stage {
-                    BuildStage::Initializing => {
-                        self.compiled_crates = 0;
-                        self.compiled_crates_server = 0;
-                        self.bundling_progress = 0.0;
-                    }
-                    BuildStage::Starting {
-                        crate_count,
-                        platform,
-                    } => {
-                        if *platform == Platform::Server {
-                            self.expected_crates_server = *crate_count;
-                        } else {
-                            self.expected_crates = *crate_count;
+                    match stage {
+                        BuildStage::Initializing => {
+                            self.compiled_crates = 0;
+                            self.compiled_crates_server = 0;
+                            self.bundling_progress = 0.0;
                         }
-                    }
-                    BuildStage::InstallingTooling {} => {}
-                    BuildStage::Compiling {
-                        current,
-                        total,
-                        platform,
-                        ..
-                    } => {
-                        if *platform == Platform::Server {
-                            self.compiled_crates_server = *current;
-                            self.expected_crates_server = *total;
-                        } else {
-                            self.compiled_crates = *current;
-                            self.expected_crates = *total;
+                        BuildStage::Starting {
+                            crate_count,
+                            is_server,
+                        } => {
+                            if *is_server {
+                                self.expected_crates_server = *crate_count;
+                            } else {
+                                self.expected_crates = *crate_count;
+                            }
                         }
+                        BuildStage::InstallingTooling {} => {}
+                        BuildStage::Compiling {
+                            current,
+                            total,
+                            is_server,
+                            ..
+                        } => {
+                            if *is_server {
+                                self.compiled_crates_server = *current;
+                                self.expected_crates_server = *total;
+                            } else {
+                                self.compiled_crates = *current;
+                                self.expected_crates = *total;
+                            }
 
-                        if self.compile_start.is_none() {
-                            self.compile_start = Some(Instant::now());
+                            if self.compile_start.is_none() {
+                                self.compile_start = Some(Instant::now());
+                            }
                         }
-                    }
-                    BuildStage::Bundling {} => {
-                        self.complete_compile();
-                        self.bundling_progress = 0.0;
-                        self.bundle_start = Some(Instant::now());
-                    }
-                    BuildStage::OptimizingWasm {} => {}
-                    BuildStage::CopyingAssets { current, total, .. } => {
-                        self.bundling_progress = *current as f64 / *total as f64;
-                    }
-                    BuildStage::Success => {
-                        self.compiled_crates = self.expected_crates;
-                        self.compiled_crates_server = self.expected_crates_server;
-                        self.bundling_progress = 1.0;
-                    }
-                    BuildStage::Failed => {
-                        self.compiled_crates = self.expected_crates;
-                        self.compiled_crates_server = self.expected_crates_server;
-                        self.bundling_progress = 1.0;
-                    }
-                    BuildStage::Aborted => {}
-                    BuildStage::Restarting => {
-                        self.compiled_crates = 0;
-                        self.compiled_crates_server = 0;
-                        self.expected_crates = 1;
-                        self.bundling_progress = 0.0;
-                    }
-                    BuildStage::RunningBindgen {} => {
-                        self.bundling_progress = 0.5;
+                        BuildStage::Bundling {} => {
+                            self.complete_compile();
+                            self.bundling_progress = 0.0;
+                            self.bundle_start = Some(Instant::now());
+                        }
+                        BuildStage::OptimizingWasm {} => {}
+                        BuildStage::CopyingAssets { current, total, .. } => {
+                            self.bundling_progress = *current as f64 / *total as f64;
+                        }
+                        BuildStage::Success => {
+                            self.compiled_crates = self.expected_crates;
+                            self.compiled_crates_server = self.expected_crates_server;
+                            self.bundling_progress = 1.0;
+                        }
+                        BuildStage::Failed => {
+                            self.compiled_crates = self.expected_crates;
+                            self.compiled_crates_server = self.expected_crates_server;
+                            self.bundling_progress = 1.0;
+                        }
+                        BuildStage::Aborted => {}
+                        BuildStage::Restarting => {
+                            self.compiled_crates = 0;
+                            self.compiled_crates_server = 0;
+                            self.expected_crates = 1;
+                            self.bundling_progress = 0.0;
+                        }
+                        BuildStage::RunningBindgen {} => {}
+                        _ => {}
                     }
                 }
             }
@@ -224,10 +215,43 @@ impl Builder {
     pub(crate) async fn finish(&mut self) -> Result<AppBundle> {
         loop {
             match self.wait().await {
-                BuildUpdate::BuildReady { bundle } => return Ok(bundle),
-                BuildUpdate::BuildFailed { err } => return Err(err),
-                BuildUpdate::Progress { .. } => {}
-                BuildUpdate::CompilerMessage { .. } => {}
+                BuildUpdate::Progress { stage } => {
+                    match &stage {
+                        BuildStage::Compiling {
+                            current,
+                            total,
+                            krate,
+                            ..
+                        } => {
+                            tracing::info!("Compiling [{current:>3}/{total}]: {krate}");
+                        }
+                        BuildStage::RunningBindgen => tracing::info!("Running wasm-bindgen..."),
+                        BuildStage::CopyingAssets {
+                            current,
+                            total,
+                            path,
+                        } => {
+                            tracing::info!("Copying asset ({current}/{total}): {}", path.display());
+                        }
+                        BuildStage::Bundling => tracing::info!("Bundling app..."),
+                        _ => {}
+                    }
+
+                    tracing::info!(json = ?StructuredOutput::BuildUpdate { stage: stage.clone() });
+                }
+                BuildUpdate::CompilerMessage { message } => {
+                    tracing::info!(json = ?StructuredOutput::CargoOutput { message: message.clone() }, %message);
+                }
+                BuildUpdate::BuildReady { bundle } => {
+                    tracing::debug!(json = ?StructuredOutput::BuildFinished {
+                        path: bundle.build.root_dir(),
+                    });
+                    return Ok(bundle);
+                }
+                BuildUpdate::BuildFailed { err } => {
+                    tracing::error!(?err, json = ?StructuredOutput::Error { message: err.to_string() });
+                    return Err(err);
+                }
             }
         }
     }
@@ -275,7 +299,7 @@ impl Builder {
         self.bundling_progress
     }
 
-    fn is_finished(&self) -> bool {
+    pub(crate) fn is_finished(&self) -> bool {
         match self.stage {
             BuildStage::Success => true,
             BuildStage::Failed => true,

+ 114 - 139
packages/cli/src/builder/bundle.rs → packages/cli/src/build/bundle.rs

@@ -3,13 +3,16 @@ use crate::{assets::AssetManifest, TraceSrc};
 use crate::{BuildRequest, Platform};
 use anyhow::Context;
 use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
-use std::sync::atomic::AtomicUsize;
 use std::{
     fs::create_dir_all,
     path::{Path, PathBuf},
 };
+use std::{sync::atomic::AtomicUsize, time::Duration};
+use tokio::process::Command;
 use wasm_bindgen_cli_support::Bindgen;
 
+use super::templates::InfoPlistData;
+
 /// The end result of a build.
 ///
 /// Contains the final asset manifest, the executables, and the workdir.
@@ -93,13 +96,14 @@ pub(crate) struct AppBundle {
 pub struct BuildArtifacts {
     pub(crate) exe: PathBuf,
     pub(crate) assets: AssetManifest,
+    pub(crate) time_taken: Duration,
 }
 
 impl AppBundle {
     /// ## Web:
     /// Create a folder that is somewhat similar to an app-image (exe + asset)
     /// The server is dropped into the `web` folder, even if there's no `public` folder.
-    /// If there's no server (SPA/static-gen), we still use the `web` folder, but it only contains the
+    /// If there's no server (SPA), we still use the `web` folder, but it only contains the
     /// public folder.
     /// ```
     /// web/
@@ -255,50 +259,38 @@ impl AppBundle {
     /// root().join(bundled)
     /// ```
     pub(crate) async fn new(
-        request: BuildRequest,
+        build: BuildRequest,
         app: BuildArtifacts,
         server: Option<BuildArtifacts>,
     ) -> Result<Self> {
-        let bundle = Self {
-            app,
-            server,
-            build: request,
-        };
+        let bundle = Self { app, server, build };
 
         tracing::debug!("Assembling app bundle");
 
         bundle.build.status_start_bundle();
-        bundle.prepare_build_dir()?;
-        bundle.write_main_executable().await?;
+        /*
+            assume the build dir is already created by BuildRequest
+            todo(jon): maybe refactor this a bit to force AppBundle to be created before it can be filled in
+        */
+        bundle
+            .write_main_executable()
+            .await
+            .context("Failed to write main executable")?;
         bundle.write_server_executable().await?;
-        bundle.write_assets().await?;
+        bundle
+            .write_assets()
+            .await
+            .context("Failed to write assets")?;
         bundle.write_metadata().await?;
         bundle.optimize().await?;
+        bundle
+            .assemble()
+            .await
+            .context("Failed to assemble app bundle")?;
 
-        Ok(bundle)
-    }
-
-    /// We only really currently care about:
-    ///
-    /// - app dir (.app, .exe, .apk, etc)
-    /// - assets dir
-    /// - exe dir (.exe, .app, .apk, etc)
-    /// - extra scaffolding
-    ///
-    /// It's not guaranteed that they're different from any other folder
-    fn prepare_build_dir(&self) -> Result<()> {
-        _ = std::fs::remove_dir_all(self.app_dir());
-
-        create_dir_all(self.app_dir())?;
-        create_dir_all(self.exe_dir())?;
-        create_dir_all(self.asset_dir())?;
-
-        // we could download the templates from somewhere (github?) but after having banged my head against
-        // cargo-mobile2 for ages, I give up with that. We're literally just going to hardcode the templates
-        // by writing them here.
-        if let Platform::Android = self.build.build.platform() {}
+        tracing::debug!("Bundle created at {}", bundle.build.root_dir().display());
 
-        Ok(())
+        Ok(bundle)
     }
 
     /// Take the output of rustc and make it into the main exe of the bundle
@@ -333,18 +325,17 @@ impl AppBundle {
             Platform::Web => {
                 // Run wasm-bindgen and drop its output into the assets folder under "dioxus"
                 self.build.status_wasm_bindgen_start();
-                self.run_wasm_bindgen(&self.app.exe.with_extension("wasm"), &self.exe_dir())
+                self.run_wasm_bindgen(&self.app.exe.with_extension("wasm"), &self.build.exe_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.status_wasm_opt_start();
-                self.run_wasm_opt(&self.exe_dir())?;
+                self.run_wasm_opt(&self.build.exe_dir())?;
 
                 // Write the index.html file with the pre-configured contents we got from pre-rendering
                 std::fs::write(
-                    self.app_dir().join("index.html"),
+                    self.build.root_dir().join("index.html"),
                     self.build.prepare_html()?,
                 )?;
             }
@@ -385,7 +376,7 @@ impl AppBundle {
             return Ok(());
         }
 
-        let asset_dir = self.asset_dir();
+        let asset_dir = self.build.asset_dir();
 
         // First, clear the asset dir
         // todo(jon): cache the asset dir, removing old files and only copying new ones that changed since the last build
@@ -418,10 +409,9 @@ impl AppBundle {
         // Parallel Copy over the assets and keep track of progress with an atomic counter
         // todo: we want to use the fastfs variant that knows how to parallelize folders, too
         assets_to_transfer.par_iter().try_for_each(|(from, to)| {
-            self.build.status_copying_asset(
-                assets_finished.fetch_add(0, std::sync::atomic::Ordering::SeqCst),
-                asset_count,
-                from.clone(),
+            tracing::trace!(
+                "Starting asset copy {current}/{asset_count} from {from:?}",
+                current = assets_finished.fetch_add(0, std::sync::atomic::Ordering::SeqCst),
             );
 
             // todo(jon): implement optimize + pre_compress on the asset type
@@ -431,7 +421,7 @@ impl AppBundle {
                 tracing::error!("Failed to copy asset {from:?}: {err}");
             }
 
-            self.build.status_copying_asset(
+            self.build.status_copied_asset(
                 assets_finished.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1,
                 asset_count,
                 from.clone(),
@@ -443,66 +433,11 @@ impl AppBundle {
         Ok(())
     }
 
-    /// The directory in which we'll put the main exe
-    ///
-    /// Mac, Android, Web are a little weird
-    /// - mac wants to be in Contents/MacOS
-    /// - android wants to be in jniLibs/arm64-v8a (or others, depending on the platform / architecture)
-    /// - web wants to be in wasm (which... we don't really need to, we could just drop the wasm into public and it would work)
-    ///
-    /// I think all others are just in the root folder
-    ///
-    /// todo(jon): investigate if we need to put .wasm in `wasm`. It kinda leaks implementation details, which ideally we don't want to do.
-    pub fn exe_dir(&self) -> PathBuf {
-        match self.build.build.platform() {
-            Platform::MacOS => self.app_dir().join("Contents").join("MacOS"),
-            Platform::Android => self.app_dir().join("jniLibs").join("arm64-v8a"),
-            Platform::Web => self.app_dir().join("wasm"),
-
-            // these are all the same, I think?
-            Platform::Windows
-            | Platform::Linux
-            | Platform::Ios
-            | Platform::Server
-            | Platform::Liveview => self.app_dir(),
-        }
-    }
-
     /// The item that we'll try to run directly if we need to.
     ///
     /// todo(jon): we should name the app properly instead of making up the exe name. It's kinda okay for dev mode, but def not okay for prod
     pub fn main_exe(&self) -> PathBuf {
-        // todo(jon): this could just be named `App` or the name of the app like `Raycast` in `Raycast.app`
-        match self.build.build.platform() {
-            Platform::MacOS => self.exe_dir().join("DioxusApp"),
-            Platform::Ios => self.exe_dir().join("DioxusApp"),
-            Platform::Server => self.exe_dir().join("server"),
-            Platform::Liveview => self.exe_dir().join("server"),
-            Platform::Windows => self.exe_dir().join("app.exe"),
-            Platform::Linux => self.exe_dir().join("AppRun"), // from the appimage spec, the root exe needs to be named `AppRun`
-            Platform::Android => self.exe_dir().join("libdioxusapp.so"), // from the apk spec, the root exe will actually be a shared library
-            Platform::Web => unimplemented!("there's no main exe on web"), // this will be wrong, I think, but not important?
-        }
-    }
-
-    pub fn asset_dir(&self) -> PathBuf {
-        match self.build.build.platform() {
-            // macos why are you weird
-            Platform::MacOS => self
-                .app_dir()
-                .join("Contents")
-                .join("Resources")
-                .join("assets"),
-
-            // everyone else is soooo normal, just app/assets :)
-            Platform::Web
-            | Platform::Ios
-            | Platform::Windows
-            | Platform::Linux
-            | Platform::Android
-            | Platform::Server
-            | Platform::Liveview => self.app_dir().join("assets"),
-        }
+        self.build.exe_dir().join(self.build.platform_exe_name())
     }
 
     /// We always put the server in the `web` folder!
@@ -531,15 +466,15 @@ impl AppBundle {
         // write the Info.plist file
         match self.build.build.platform() {
             Platform::MacOS => {
-                let src = include_str!("../../assets/macos/mac.plist");
-                let dest = self.app_dir().join("Contents").join("Info.plist");
-                std::fs::write(dest, src)?;
+                let dest = self.build.root_dir().join("Contents").join("Info.plist");
+                let plist = self.macos_plist_contents()?;
+                std::fs::write(dest, plist)?;
             }
 
             Platform::Ios => {
-                let src = include_str!("../../assets/ios/ios.plist");
-                let dest = self.app_dir().join("Info.plist");
-                std::fs::write(dest, src)?;
+                let dest = self.build.root_dir().join("Info.plist");
+                let plist = self.ios_plist_contents()?;
+                std::fs::write(dest, plist)?;
             }
 
             // AndroidManifest.xml
@@ -575,7 +510,7 @@ impl AppBundle {
                     .krate
                     .should_pre_compress_web_assets(self.build.build.release);
 
-                let bindgen_dir = self.exe_dir();
+                let bindgen_dir = self.build.exe_dir();
                 tokio::task::spawn_blocking(move || {
                     crate::fastfs::pre_compress_folder(&bindgen_dir, pre_compress)
                 })
@@ -618,37 +553,6 @@ impl AppBundle {
         None
     }
 
-    /// returns the path to .app/.apk/.appimage folder
-    ///
-    /// we only add an extension to the folders where it sorta matters that it's named with the extension.
-    /// for example, on mac, the `.app` indicates we can `open` it and it pulls in icons, dylibs, etc.
-    ///
-    /// for our simulator-based platforms, this is less important since they need to be zipped up anyways
-    /// to run in the simulator.
-    ///
-    /// For windows/linux, it's also not important since we're just running the exe directly out of the folder
-    pub(crate) fn app_dir(&self) -> PathBuf {
-        let platform_dir = self
-            .build
-            .krate
-            .build_dir(self.build.build.platform(), self.build.build.release);
-
-        match self.build.build.platform() {
-            Platform::Web => platform_dir.join("public"),
-            Platform::Server => platform_dir.clone(), // ends up *next* to the public folder
-
-            // These might not actually need to be called `.app` but it does let us run these with `open`
-            Platform::MacOS => platform_dir.join("DioxusApp.app"),
-            Platform::Ios => platform_dir.join("DioxusApp.app"),
-
-            // in theory, these all could end up in the build dir
-            Platform::Linux => platform_dir.join("app"), // .appimage (after bundling)
-            Platform::Windows => platform_dir.join("app"), // .exe (after bundling)
-            Platform::Android => platform_dir.join("app"), // .apk (after bundling)
-            Platform::Liveview => platform_dir.join("app"), // .exe (after bundling)
-        }
-    }
-
     pub(crate) async fn run_wasm_bindgen(
         &self,
         input_path: &Path,
@@ -691,7 +595,6 @@ impl AppBundle {
         if !self.build.build.release {
             return Ok(());
         };
-
         self.build.status_optimizing_wasm();
 
         #[cfg(feature = "optimizations")]
@@ -799,4 +702,76 @@ impl AppBundle {
 
         Ok(())
     }
+
+    pub(crate) fn bundle_identifier(&self) -> String {
+        format!("com.dioxuslabs.{}", self.build.krate.executable_name())
+    }
+
+    fn macos_plist_contents(&self) -> Result<String> {
+        handlebars::Handlebars::new()
+            .render_template(
+                include_str!("../../assets/macos/mac.plist.hbs"),
+                &InfoPlistData {
+                    display_name: self.build.platform_exe_name(),
+                    bundle_name: self.build.platform_exe_name(),
+                    executable_name: self.build.platform_exe_name(),
+                    bundle_identifier: format!("com.dioxuslabs.{}", self.build.platform_exe_name()),
+                },
+            )
+            .map_err(|e| e.into())
+    }
+
+    fn ios_plist_contents(&self) -> Result<String> {
+        handlebars::Handlebars::new()
+            .render_template(
+                include_str!("../../assets/ios/ios.plist.hbs"),
+                &InfoPlistData {
+                    display_name: self.build.platform_exe_name(),
+                    bundle_name: self.build.platform_exe_name(),
+                    executable_name: self.build.platform_exe_name(),
+                    bundle_identifier: format!("com.dioxuslabs.{}", self.build.platform_exe_name()),
+                },
+            )
+            .map_err(|e| e.into())
+    }
+
+    /// Run any final tools to produce apks or other artifacts we might need.
+    async fn assemble(&self) -> Result<()> {
+        if let Platform::Android = self.build.build.platform() {
+            // make sure we can execute the gradlew script
+            #[cfg(unix)]
+            {
+                use std::os::unix::prelude::PermissionsExt;
+                std::fs::set_permissions(
+                    self.build.root_dir().join("gradlew"),
+                    std::fs::Permissions::from_mode(0o755),
+                )?;
+            }
+
+            let output = Command::new("./gradlew")
+                .arg("assembleDebug")
+                .current_dir(self.build.root_dir())
+                .stderr(std::process::Stdio::piped())
+                .stdout(std::process::Stdio::piped())
+                .output()
+                .await?;
+
+            if !output.status.success() {
+                return Err(anyhow::anyhow!("Failed to assemble apk: {output:?}").into());
+            }
+        }
+
+        Ok(())
+    }
+
+    pub(crate) fn apk_path(&self) -> PathBuf {
+        self.build
+            .root_dir()
+            .join("app")
+            .join("build")
+            .join("outputs")
+            .join("apk")
+            .join("debug")
+            .join("app-debug.apk")
+    }
 }

+ 5 - 4
packages/cli/src/builder/mod.rs → packages/cli/src/build/mod.rs

@@ -5,14 +5,15 @@
 //! Uses a request -> response architecture that allows you to monitor the progress with an optional message
 //! receiver.
 
-mod build;
+mod builder;
 mod bundle;
 mod progress;
-mod runner;
+mod request;
+mod templates;
 mod verify;
 mod web;
 
-pub(crate) use build::*;
+pub(crate) use builder::*;
 pub(crate) use bundle::*;
 pub(crate) use progress::*;
-pub(crate) use runner::*;
+pub(crate) use request::*;

+ 9 - 46
packages/cli/src/builder/progress.rs → packages/cli/src/build/progress.rs

@@ -1,5 +1,5 @@
 //! Report progress about the build to the user. We use channels to report progress back to the CLI.
-use crate::{AppBundle, BuildRequest, Platform, TraceSrc};
+use crate::{AppBundle, BuildRequest, BuildStage, Platform, TraceSrc};
 use cargo_metadata::CompilerMessage;
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use std::path::PathBuf;
@@ -16,46 +16,12 @@ pub(crate) enum BuildUpdate {
     BuildFailed { err: crate::Error },
 }
 
-#[non_exhaustive]
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum BuildStage {
-    Initializing,
-    Starting {
-        platform: Platform,
-        crate_count: usize,
-    },
-    InstallingTooling {},
-    Compiling {
-        platform: Platform,
-        current: usize,
-        total: usize,
-        krate: String,
-    },
-    Bundling {},
-    RunningBindgen {},
-    OptimizingWasm {},
-    CopyingAssets {
-        current: usize,
-        total: usize,
-        path: PathBuf,
-    },
-    Success,
-    Failed,
-    Aborted,
-    Restarting,
-}
-
 impl BuildRequest {
     pub(crate) fn status_wasm_bindgen_start(&self) {
         _ = self.progress.unbounded_send(BuildUpdate::Progress {
             stage: BuildStage::RunningBindgen {},
         });
     }
-    pub(crate) fn status_wasm_opt_start(&self) {
-        _ = self.progress.unbounded_send(BuildUpdate::Progress {
-            stage: BuildStage::RunningBindgen {},
-        });
-    }
 
     pub(crate) fn status_start_bundle(&self) {
         _ = self.progress.unbounded_send(BuildUpdate::Progress {
@@ -73,19 +39,13 @@ impl BuildRequest {
         tracing::trace!(dx_src = ?TraceSrc::Cargo, "{line}");
     }
 
-    pub(crate) fn status_build_progress(
-        &self,
-        count: usize,
-        total: usize,
-        name: String,
-        platform: Platform,
-    ) {
+    pub(crate) fn status_build_progress(&self, count: usize, total: usize, name: String) {
         _ = self.progress.unbounded_send(BuildUpdate::Progress {
             stage: BuildStage::Compiling {
                 current: count,
                 total,
                 krate: name,
-                platform,
+                is_server: self.is_server(),
             },
         });
     }
@@ -93,14 +53,13 @@ impl BuildRequest {
     pub(crate) fn status_starting_build(&self, crate_count: usize) {
         _ = self.progress.unbounded_send(BuildUpdate::Progress {
             stage: BuildStage::Starting {
-                platform: self.build.platform(),
+                is_server: self.build.platform() == Platform::Server,
                 crate_count,
             },
         });
     }
 
-    pub(crate) fn status_copying_asset(&self, current: usize, total: usize, path: PathBuf) {
-        tracing::trace!("Status copying asset {current}/{total} from {path:?}");
+    pub(crate) fn status_copied_asset(&self, current: usize, total: usize, path: PathBuf) {
         _ = self.progress.unbounded_send(BuildUpdate::Progress {
             stage: BuildStage::CopyingAssets {
                 current,
@@ -121,4 +80,8 @@ impl BuildRequest {
             stage: BuildStage::InstallingTooling {},
         });
     }
+
+    pub(crate) fn is_server(&self) -> bool {
+        self.build.platform() == Platform::Server
+    }
 }

+ 377 - 28
packages/cli/src/builder/build.rs → packages/cli/src/build/request.rs

@@ -2,14 +2,11 @@ use super::{progress::ProgressTx, BuildArtifacts};
 use crate::dioxus_crate::DioxusCrate;
 use crate::Result;
 use crate::{assets::AssetManifest, TraceSrc};
-use crate::{build::BuildArgs, link::LinkAction};
+use crate::{link::LinkAction, BuildArgs};
 use crate::{AppBundle, Platform};
 use anyhow::Context;
 use serde::Deserialize;
-use std::{
-    path::{Path, PathBuf},
-    process::Stdio,
-};
+use std::{path::PathBuf, process::Stdio, time::Instant};
 use tokio::{io::AsyncBufReadExt, process::Command};
 
 #[derive(Clone, Debug)]
@@ -44,17 +41,32 @@ impl BuildRequest {
     pub(crate) async fn build_all(self) -> Result<AppBundle> {
         tracing::debug!("Running build command...");
 
+        let (app, server) = self.build_concurrent().await?;
+
+        AppBundle::new(self, app, server).await
+    }
+
+    /// Run the build command with a pretty loader, returning the executable output location
+    async fn build_concurrent(&self) -> Result<(BuildArtifacts, Option<BuildArtifacts>)> {
         let (app, server) =
             futures_util::future::try_join(self.build_app(), self.build_server()).await?;
 
-        AppBundle::new(self, app, server).await
+        Ok((app, server))
     }
 
     pub(crate) async fn build_app(&self) -> Result<BuildArtifacts> {
         tracing::debug!("Building app...");
+
+        let start = Instant::now();
+        self.prepare_build_dir()?;
         let exe = self.build_cargo().await?;
-        let assets = self.collect_assets(&exe).await?;
-        Ok(BuildArtifacts { exe, assets })
+        let assets = self.collect_assets().await?;
+
+        Ok(BuildArtifacts {
+            exe,
+            assets,
+            time_taken: start.elapsed(),
+        })
     }
 
     pub(crate) async fn build_server(&self) -> Result<Option<BuildArtifacts>> {
@@ -87,8 +99,9 @@ impl BuildRequest {
             .current_dir(self.krate.crate_dir())
             .arg("--message-format")
             .arg("json-diagnostic-rendered-ansi")
-            .args(self.build_arguments());
-        // .env("RUSTFLAGS", self.rust_flags());
+            .args(self.build_arguments())
+            .envs(self.env_vars());
+        // .args(["--", "-Csave-temps=y"]);
 
         if let Some(target_dir) = self.custom_target_dir.as_ref() {
             cmd.env("CARGO_TARGET_DIR", target_dir);
@@ -153,7 +166,6 @@ impl BuildRequest {
                             units_compiled,
                             crate_count,
                             artifact.target.name,
-                            self.build.platform(),
                         ),
                     }
                 }
@@ -188,19 +200,14 @@ 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(crate) async fn collect_assets(&self, exe: &Path) -> Result<AssetManifest> {
+    pub(crate) async fn collect_assets(&self) -> Result<AssetManifest> {
         tracing::debug!("Collecting assets ...");
 
-        // If assets are skipped, we don't need to collect them
         if self.build.skip_assets {
             return Ok(AssetManifest::default());
         }
 
-        let mut manifest = AssetManifest::default();
-
-        _ = manifest.add_from_object_path(exe.to_path_buf());
-
-        Ok(manifest)
+        self.deep_linker_asset_extract().await
     }
 
     /// Create a list of arguments for cargo builds
@@ -254,11 +261,8 @@ impl BuildRequest {
             }
         }
 
-        if self.build.verbose {
-            cargo_args.push("--verbose".to_string());
-        } else {
-            cargo_args.push("--quiet".to_string());
-        }
+        // We always run in verbose since the CLI itself is the one doing the presentation
+        cargo_args.push("--verbose".to_string());
 
         if self.build.target_args.no_default_features {
             cargo_args.push("--no-default-features".to_string());
@@ -293,7 +297,7 @@ impl BuildRequest {
     }
 
     #[allow(dead_code)]
-    pub(crate) fn rust_flags(&self) -> String {
+    pub(crate) fn android_rust_flags(&self) -> String {
         let mut rust_flags = std::env::var("RUSTFLAGS").unwrap_or_default();
 
         if self.build.platform() == Platform::Android {
@@ -355,6 +359,7 @@ impl BuildRequest {
             .arg("-Z")
             .arg("unstable-options")
             .args(self.build_arguments())
+            .envs(self.env_vars())
             .stdout(Stdio::piped())
             .stderr(Stdio::piped())
             .output()
@@ -394,7 +399,6 @@ impl BuildRequest {
     ///
     /// There's a chance that's not actually true, so this function is kept around in case we do
     /// need to revert to "deep extraction".
-    #[allow(unused)]
     async fn deep_linker_asset_extract(&self) -> Result<AssetManifest> {
         // 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
@@ -406,9 +410,9 @@ impl BuildRequest {
         //
         // This will force `dx` to look through the incremental cache and find the assets from the previous build
         Command::new("cargo")
-            // .env("RUSTFLAGS", self.rust_flags())
             .arg("rustc")
             .args(self.build_arguments())
+            .envs(self.env_vars())
             .arg("--offline") /* don't use the network, should already be resolved */
             .arg("--")
             .arg(format!(
@@ -422,7 +426,7 @@ impl BuildRequest {
             .env(
                 LinkAction::ENV_VAR_NAME,
                 LinkAction::BuildAssetManifest {
-                    destination: tmp_file.path().to_path_buf(),
+                    destination: tmp_file.path().to_path_buf().clone(),
                 }
                 .to_json(),
             )
@@ -432,6 +436,351 @@ impl BuildRequest {
             .await?;
 
         // The linker wrote the manifest to the temp file, let's load it!
-        Ok(AssetManifest::load_from_file(tmp_file.path())?)
+        let manifest = AssetManifest::load_from_file(tmp_file.path())?;
+
+        if let Ok(path) = std::env::var("DEEPLINK").map(|s| s.parse::<PathBuf>().unwrap()) {
+            _ = tmp_file.persist(path);
+        }
+
+        Ok(manifest)
+    }
+
+    fn env_vars(&self) -> Vec<(&str, String)> {
+        let mut env_vars = vec![];
+
+        if self.build.platform() == Platform::Android {
+            let app = self.root_dir().join("app");
+            let app_main = app.join("src").join("main");
+            let app_kotlin = app_main.join("kotlin");
+            let app_kotlin_out = app_kotlin.join("com").join("example").join("androidfinal");
+
+            env_vars.push((
+                "WRY_ANDROID_PACKAGE",
+                "com.example.androidfinal".to_string(),
+            ));
+            env_vars.push(("WRY_ANDROID_LIBRARY", "androidfinal".to_string()));
+            env_vars.push((
+                "WRY_ANDROID_KOTLIN_FILES_OUT_DIR",
+                app_kotlin_out.display().to_string(),
+            ));
+
+            env_vars.push(("RUSTFLAGS", self.android_rust_flags()))
+        };
+
+        env_vars
+    }
+
+    /// We only really currently care about:
+    ///
+    /// - app dir (.app, .exe, .apk, etc)
+    /// - assets dir
+    /// - exe dir (.exe, .app, .apk, etc)
+    /// - extra scaffolding
+    ///
+    /// It's not guaranteed that they're different from any other folder
+    fn prepare_build_dir(&self) -> Result<()> {
+        use once_cell::sync::OnceCell;
+        use std::fs::{create_dir_all, remove_dir_all};
+
+        static INITIALIZED: OnceCell<Result<()>> = OnceCell::new();
+
+        let success = INITIALIZED.get_or_init(|| {
+            _ = remove_dir_all(self.root_dir());
+
+            create_dir_all(self.root_dir())?;
+            create_dir_all(self.exe_dir())?;
+            create_dir_all(self.asset_dir())?;
+
+            tracing::debug!("Initialized Root dir: {:?}", self.root_dir());
+            tracing::debug!("Initialized Exe dir: {:?}", self.exe_dir());
+            tracing::debug!("Initialized Asset dir: {:?}", self.asset_dir());
+
+            // we could download the templates from somewhere (github?) but after having banged my head against
+            // cargo-mobile2 for ages, I give up with that. We're literally just going to hardcode the templates
+            // by writing them here.
+            if let Platform::Android = self.build.platform() {
+                self.build_android_app_dir()?;
+            }
+
+            Ok(())
+        });
+
+        if let Err(e) = success.as_ref() {
+            return Err(format!("Failed to initialize build directory: {e}").into());
+        }
+
+        Ok(())
+    }
+
+    /// The directory in which we'll put the main exe
+    ///
+    /// Mac, Android, Web are a little weird
+    /// - mac wants to be in Contents/MacOS
+    /// - android wants to be in jniLibs/arm64-v8a (or others, depending on the platform / architecture)
+    /// - web wants to be in wasm (which... we don't really need to, we could just drop the wasm into public and it would work)
+    ///
+    /// I think all others are just in the root folder
+    ///
+    /// todo(jon): investigate if we need to put .wasm in `wasm`. It kinda leaks implementation details, which ideally we don't want to do.
+    pub fn exe_dir(&self) -> PathBuf {
+        match self.build.platform() {
+            Platform::MacOS => self.root_dir().join("Contents").join("MacOS"),
+            Platform::Web => self.root_dir().join("wasm"),
+
+            // Android has a whole build structure to it
+            Platform::Android => self
+                .root_dir()
+                .join("app")
+                .join("src")
+                .join("main")
+                .join("jniLibs")
+                .join("arm64-v8a"),
+
+            // these are all the same, I think?
+            Platform::Windows
+            | Platform::Linux
+            | Platform::Ios
+            | Platform::Server
+            | Platform::Liveview => self.root_dir(),
+        }
+    }
+
+    /// returns the path to root build folder. This will be our working directory for the build.
+    ///
+    /// we only add an extension to the folders where it sorta matters that it's named with the extension.
+    /// for example, on mac, the `.app` indicates we can `open` it and it pulls in icons, dylibs, etc.
+    ///
+    /// for our simulator-based platforms, this is less important since they need to be zipped up anyways
+    /// to run in the simulator.
+    ///
+    /// For windows/linux, it's also not important since we're just running the exe directly out of the folder
+    ///
+    /// The idea of this folder is that we can run our top-level build command against it and we'll get
+    /// a final build output somewhere. Some platforms have basically no build command, and can simply
+    /// be ran by executing the exe directly.
+    pub(crate) fn root_dir(&self) -> PathBuf {
+        let platform_dir = self
+            .krate
+            .build_dir(self.build.platform(), self.build.release);
+
+        match self.build.platform() {
+            Platform::Web => platform_dir.join("public"),
+            Platform::Server => platform_dir.clone(), // ends up *next* to the public folder
+
+            // These might not actually need to be called `.app` but it does let us run these with `open`
+            Platform::MacOS => platform_dir.join(format!("{}.app", self.platform_exe_name())),
+            Platform::Ios => platform_dir.join(format!("{}.app", self.platform_exe_name())),
+
+            // in theory, these all could end up directly in the root dir
+            Platform::Android => platform_dir.join("app"), // .apk (after bundling)
+            Platform::Linux => platform_dir.join("app"),   // .appimage (after bundling)
+            Platform::Windows => platform_dir.join("app"), // .exe (after bundling)
+            Platform::Liveview => platform_dir.join("app"), // .exe (after bundling)
+        }
+    }
+
+    pub fn asset_dir(&self) -> PathBuf {
+        match self.build.platform() {
+            Platform::MacOS => self
+                .root_dir()
+                .join("Contents")
+                .join("Resources")
+                .join("assets"),
+
+            Platform::Android => self
+                .root_dir()
+                .join("app")
+                .join("src")
+                .join("main")
+                .join("assets"),
+
+            // everyone else is soooo normal, just app/assets :)
+            Platform::Web
+            | Platform::Ios
+            | Platform::Windows
+            | Platform::Linux
+            | Platform::Server
+            | Platform::Liveview => self.root_dir().join("assets"),
+        }
+    }
+
+    pub fn platform_exe_name(&self) -> String {
+        match self.build.platform() {
+            Platform::MacOS => self.krate.executable_name().to_string(),
+            Platform::Ios => self.krate.executable_name().to_string(),
+            Platform::Server => self.krate.executable_name().to_string(),
+            Platform::Liveview => self.krate.executable_name().to_string(),
+            Platform::Windows => format!("{}.exe", self.krate.executable_name()),
+
+            // from the apk spec, the root exe will actually be a shared library
+            Platform::Android => format!("lib{}.so", self.krate.executable_name()),
+            Platform::Web => unimplemented!("there's no main exe on web"), // this will be wrong, I think, but not important?
+
+            // todo: maybe this should be called AppRun?
+            Platform::Linux => self.krate.executable_name().to_string(),
+        }
+    }
+
+    fn build_android_app_dir(&self) -> Result<()> {
+        use std::fs::{create_dir_all, write};
+        let root = self.root_dir();
+
+        // gradle
+        let wrapper = root.join("gradle").join("wrapper");
+        create_dir_all(&wrapper)?;
+        tracing::debug!("Initialized Gradle wrapper: {:?}", wrapper);
+
+        // app
+        let app = root.join("app");
+        let app_main = app.join("src").join("main");
+        let app_kotlin = app_main.join("kotlin");
+        let app_jnilibs = app_main.join("jniLibs");
+        let app_assets = app_main.join("assets");
+        let app_kotlin_out = app_kotlin.join("com").join("example").join("androidfinal");
+        create_dir_all(&app)?;
+        create_dir_all(&app_main)?;
+        create_dir_all(&app_kotlin)?;
+        create_dir_all(&app_jnilibs)?;
+        create_dir_all(&app_assets)?;
+        create_dir_all(&app_kotlin_out)?;
+        tracing::debug!("Initialized app: {:?}", app);
+        tracing::debug!("Initialized app/src: {:?}", app_main);
+        tracing::debug!("Initialized app/src/kotlin: {:?}", app_kotlin);
+        tracing::debug!("Initialized app/src/jniLibs: {:?}", app_jnilibs);
+        tracing::debug!("Initialized app/src/assets: {:?}", app_assets);
+        tracing::debug!("Initialized app/src/kotlin/main: {:?}", app_kotlin_out);
+
+        // Top-level gradle config
+        write(
+            root.join("build.gradle.kts"),
+            include_bytes!("../../assets/android/gen/build.gradle.kts"),
+        )?;
+        write(
+            root.join("gradle.properties"),
+            include_bytes!("../../assets/android/gen/gradle.properties"),
+        )?;
+        write(
+            root.join("gradlew"),
+            include_bytes!("../../assets/android/gen/gradlew"),
+        )?;
+        write(
+            root.join("gradlew.bat"),
+            include_bytes!("../../assets/android/gen/gradlew.bat"),
+        )?;
+        write(
+            root.join("settings.gradle"),
+            include_bytes!("../../assets/android/gen/settings.gradle"),
+        )?;
+
+        // Then the wrapper and its properties
+        write(
+            wrapper.join("gradle-wrapper.properties"),
+            include_bytes!("../../assets/android/gen/gradle/wrapper/gradle-wrapper.properties"),
+        )?;
+        write(
+            wrapper.join("gradle-wrapper.jar"),
+            include_bytes!("../../assets/android/gen/gradle/wrapper/gradle-wrapper.jar"),
+        )?;
+
+        // Now the app directory
+        write(
+            app.join("build.gradle.kts"),
+            include_bytes!("../../assets/android/gen/app/build.gradle.kts"),
+        )?;
+        write(
+            app.join("proguard-rules.pro"),
+            include_bytes!("../../assets/android/gen/app/proguard-rules.pro"),
+        )?;
+        write(
+            app.join("src").join("main").join("AndroidManifest.xml"),
+            include_bytes!("../../assets/android/gen/app/src/main/AndroidManifest.xml"),
+        )?;
+
+        // Write the main activity manually since tao dropped support for it
+        write(
+            app_main
+                .join("kotlin")
+                .join("com")
+                .join("example")
+                .join("androidfinal")
+                .join("MainActivity.kt"),
+            include_bytes!("../../assets/android/MainActivity.kt"),
+        )?;
+
+        // Write the res folder
+        let res = app_main.join("res");
+        create_dir_all(&res)?;
+        create_dir_all(res.join("values"))?;
+        write(
+            res.join("values").join("strings.xml"),
+            include_bytes!("../../assets/android/gen/app/src/main/res/values/strings.xml"),
+        )?;
+        write(
+            res.join("values").join("colors.xml"),
+            include_bytes!("../../assets/android/gen/app/src/main/res/values/colors.xml"),
+        )?;
+        write(
+            res.join("values").join("styles.xml"),
+            include_bytes!("../../assets/android/gen/app/src/main/res/values/styles.xml"),
+        )?;
+
+        create_dir_all(res.join("drawable"))?;
+        write(
+            res.join("drawable").join("ic_launcher_background.xml"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/drawable/ic_launcher_background.xml"
+            ),
+        )?;
+        create_dir_all(res.join("drawable-v24"))?;
+        write(
+            res.join("drawable-v24").join("ic_launcher_foreground.xml"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/drawable-v24/ic_launcher_foreground.xml"
+            ),
+        )?;
+        create_dir_all(res.join("mipmap-anydpi-v26"))?;
+        write(
+            res.join("mipmap-anydpi-v26").join("ic_launcher.xml"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml"
+            ),
+        )?;
+        create_dir_all(res.join("mipmap-hdpi"))?;
+        write(
+            res.join("mipmap-hdpi").join("ic_launcher.webp"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/mipmap-hdpi/ic_launcher.webp"
+            ),
+        )?;
+        create_dir_all(res.join("mipmap-mdpi"))?;
+        write(
+            res.join("mipmap-mdpi").join("ic_launcher.webp"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/mipmap-mdpi/ic_launcher.webp"
+            ),
+        )?;
+        create_dir_all(res.join("mipmap-xhdpi"))?;
+        write(
+            res.join("mipmap-xhdpi").join("ic_launcher.webp"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/mipmap-xhdpi/ic_launcher.webp"
+            ),
+        )?;
+        create_dir_all(res.join("mipmap-xxhdpi"))?;
+        write(
+            res.join("mipmap-xxhdpi").join("ic_launcher.webp"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp"
+            ),
+        )?;
+        create_dir_all(res.join("mipmap-xxxhdpi"))?;
+        write(
+            res.join("mipmap-xxxhdpi").join("ic_launcher.webp"),
+            include_bytes!(
+                "../../assets/android/gen/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp"
+            ),
+        )?;
+
+        Ok(())
     }
 }

+ 7 - 0
packages/cli/src/build/templates.rs

@@ -0,0 +1,7 @@
+#[derive(serde::Serialize)]
+pub struct InfoPlistData {
+    pub display_name: String,
+    pub bundle_name: String,
+    pub bundle_identifier: String,
+    pub executable_name: String,
+}

+ 12 - 12
packages/cli/src/builder/verify.rs → packages/cli/src/build/verify.rs

@@ -94,21 +94,21 @@ impl BuildRequest {
     /// should be installing the x86 versions.
     pub(crate) async fn verify_ios_tooling(&self, _rustup: RustupShow) -> Result<()> {
         // open the simulator
-        _ = tokio::process::Command::new("open")
-            .arg("/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app")
-            .stderr(Stdio::piped())
-            .stdout(Stdio::piped())
-            .status()
-            .await;
+        // _ = tokio::process::Command::new("open")
+        //     .arg("/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app")
+        //     .stderr(Stdio::piped())
+        //     .stdout(Stdio::piped())
+        //     .status()
+        //     .await;
 
         // Now xcrun to open the device
         // todo: we should try and query the device list and/or parse it rather than hardcode this simulator
-        _ = tokio::process::Command::new("xcrun")
-            .args(["simctl", "boot", "83AE3067-987F-4F85-AE3D-7079EF48C967"])
-            .stderr(Stdio::piped())
-            .stdout(Stdio::piped())
-            .status()
-            .await;
+        // _ = tokio::process::Command::new("xcrun")
+        //     .args(["simctl", "boot", "83AE3067-987F-4F85-AE3D-7079EF48C967"])
+        //     .stderr(Stdio::piped())
+        //     .stdout(Stdio::piped())
+        //     .status()
+        //     .await;
 
         // if !rustup
         //     .installed_toolchains

+ 0 - 0
packages/cli/src/builder/web.rs → packages/cli/src/build/web.rs


+ 1 - 6
packages/cli/src/bundle_utils.rs

@@ -3,12 +3,6 @@ use crate::{
     NSISInstallerMode, NsisSettings, PackageType, WebviewInstallMode, WindowsSettings, WixSettings,
 };
 
-pub(crate) fn make_tauri_bundler_settings(
-    bundle_config: BundleConfig,
-) -> tauri_bundler::BundleSettings {
-    bundle_config.into()
-}
-
 impl From<NsisSettings> for tauri_bundler::NsisSettings {
     fn from(val: NsisSettings) -> Self {
         tauri_bundler::NsisSettings {
@@ -162,6 +156,7 @@ impl From<PackageType> for tauri_bundler::PackageType {
             PackageType::AppImage => Self::AppImage,
             PackageType::Dmg => Self::Dmg,
             PackageType::Updater => Self::Updater,
+            PackageType::Nsis => Self::Nsis,
         }
     }
 }

+ 14 - 23
packages/cli/src/cli/autoformat.rs

@@ -1,8 +1,9 @@
 use super::*;
 use crate::DioxusCrate;
+use anyhow::Context;
 use dioxus_autofmt::{IndentOptions, IndentType};
 use rayon::prelude::*;
-use std::{borrow::Cow, fs, path::Path, process::exit};
+use std::{borrow::Cow, fs, path::Path};
 
 // For reference, the rustfmt main.rs file
 // https://github.com/rust-lang/rustfmt/blob/master/src/bin/main.rs
@@ -37,7 +38,7 @@ pub(crate) struct Autoformat {
 }
 
 impl Autoformat {
-    pub(crate) fn autoformat(self) -> Result<()> {
+    pub(crate) fn autoformat(self) -> Result<StructuredOutput> {
         let Autoformat {
             check,
             raw,
@@ -56,9 +57,7 @@ impl Autoformat {
             if let Some(inner) = dioxus_autofmt::fmt_block(&raw, 0, indent) {
                 println!("{}", inner);
             } else {
-                // exit process with error
-                eprintln!("error formatting codeblock");
-                exit(1);
+                return Err("error formatting codeblock".into());
             }
         } else {
             // Default to formatting the project.
@@ -68,13 +67,9 @@ impl Autoformat {
                     package: Some(package),
                     ..Default::default()
                 };
-                let dx_crate = match DioxusCrate::new(&target_args) {
-                    Ok(x) => x,
-                    Err(error) => {
-                        eprintln!("failed to parse crate graph: {error}");
-                        exit(1);
-                    }
-                };
+                let dx_crate =
+                    DioxusCrate::new(&target_args).context("failed to parse crate graph")?;
+
                 Cow::Owned(dx_crate.crate_dir())
             } else {
                 Cow::Borrowed(Path::new("."))
@@ -83,12 +78,11 @@ impl Autoformat {
             if let Err(e) =
                 autoformat_project(check, split_line_attributes, format_rust_code, crate_dir)
             {
-                eprintln!("error formatting project: {}", e);
-                exit(1);
+                return Err(format!("error formatting project: {}", e).into());
             }
         }
 
-        Ok(())
+        Ok(StructuredOutput::Success)
     }
 }
 
@@ -106,8 +100,7 @@ fn refactor_file(
         fs::read_to_string(&file)
     };
     let Ok(mut s) = file_content else {
-        eprintln!("failed to open file: {}", file_content.unwrap_err());
-        exit(1);
+        return Err(format!("failed to open file: {}", file_content.unwrap_err()).into());
     };
 
     if format_rust_code {
@@ -117,8 +110,7 @@ fn refactor_file(
     let Ok(Ok(edits)) =
         syn::parse_file(&s).map(|file| dioxus_autofmt::try_fmt_file(&s, &file, indent))
     else {
-        eprintln!("failed to format file: {}", s);
-        exit(1);
+        return Err(format!("failed to format file: {}", s).into());
     };
 
     let out = dioxus_autofmt::apply_formats(&s, edits);
@@ -126,7 +118,7 @@ fn refactor_file(
     if file == "-" {
         print!("{}", out);
     } else if let Err(e) = fs::write(&file, out) {
-        eprintln!("failed to write formatted content to file: {e}",);
+        tracing::error!("failed to write formatted content to file: {e}",);
     } else {
         println!("formatted {}", file);
     }
@@ -212,7 +204,7 @@ fn autoformat_project(
             match res {
                 Ok(cnt) => Some(cnt),
                 Err(err) => {
-                    eprintln!("error formatting file : {}\n{:#?}", path.display(), err);
+                    tracing::error!("error formatting file : {}\n{:#?}", path.display(), err);
                     None
                 }
             }
@@ -222,8 +214,7 @@ fn autoformat_project(
     let files_formatted: usize = counts.into_iter().flatten().sum();
 
     if files_formatted > 0 && check {
-        eprintln!("{} files needed formatting", files_formatted);
-        exit(1);
+        return Err(format!("{} files needed formatting", files_formatted).into());
     }
 
     Ok(())

+ 10 - 30
packages/cli/src/cli/build.rs

@@ -1,11 +1,10 @@
 use super::*;
-use crate::{AppBundle, Builder, DioxusCrate, Platform, PROFILE_SERVER};
+use crate::{Builder, DioxusCrate, Platform, PROFILE_SERVER};
 
 /// 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 {
     /// Build in release mode [default: false]
     #[clap(long, short)]
@@ -17,21 +16,6 @@ pub(crate) struct BuildArgs {
     #[serde(default)]
     pub(crate) force_sequential: bool,
 
-    /// Use verbose output [default: false]
-    #[clap(long)]
-    #[serde(default)]
-    pub(crate) verbose: bool,
-
-    /// Use trace output [default: false]
-    #[clap(long)]
-    #[serde(default)]
-    pub(crate) trace: bool,
-
-    /// Pass -Awarnings to the cargo build
-    #[clap(long)]
-    #[serde(default)]
-    pub(crate) silent: bool,
-
     /// Build the app with custom a profile
     #[clap(long)]
     pub(crate) profile: Option<String>,
@@ -73,12 +57,9 @@ pub(crate) struct BuildArgs {
 }
 
 impl BuildArgs {
-    pub async fn build_it(&mut self) -> Result<()> {
-        self.build().await?;
-        Ok(())
-    }
+    pub async fn run_cmd(mut self) -> Result<StructuredOutput> {
+        tracing::info!("Building project...");
 
-    pub(crate) async fn build(&mut self) -> Result<AppBundle> {
         let krate =
             DioxusCrate::new(&self.target_args).context("Failed to load Dioxus workspace")?;
 
@@ -86,12 +67,11 @@ impl BuildArgs {
 
         let bundle = Builder::start(&krate, self.clone())?.finish().await?;
 
-        println!(
-            "Successfully built! 💫\nBundle at {}",
-            bundle.app_dir().display()
-        );
+        tracing::info!(path = ?bundle.build.root_dir(), "Build completed successfully! 🚀");
 
-        Ok(bundle)
+        Ok(StructuredOutput::BuildFinished {
+            path: bundle.build.root_dir(),
+        })
     }
 
     /// Update the arguments of the CLI by inspecting the DioxusCrate itself and learning about how
@@ -127,13 +107,13 @@ impl BuildArgs {
         // Add any features required to turn on the client
         self.target_args
             .client_features
-            .extend(krate.feature_for_platform(platform));
+            .push(krate.feature_for_platform(platform));
 
         // Add any features required to turn on the server
         // This won't take effect in the server is not built, so it's fine to just set it here even if it's not used
         self.target_args
             .server_features
-            .extend(krate.feature_for_platform(Platform::Server));
+            .push(krate.feature_for_platform(Platform::Server));
 
         // Make sure we set the fullstack platform so we actually build the fullstack variant
         // Users need to enable "fullstack" in their default feature set.
@@ -150,7 +130,7 @@ impl BuildArgs {
 
         // Set the profile of the build if it's not already set
         // We do this for android/wasm since they require
-        if self.profile.is_none() {
+        if self.profile.is_none() && !self.release {
             match self.platform {
                 Some(Platform::Android) => {
                     self.profile = Some(crate::dioxus_crate::PROFILE_ANDROID.to_string());

+ 162 - 75
packages/cli/src/cli/bundle.rs

@@ -1,20 +1,36 @@
-use crate::bundle_utils::make_tauri_bundler_settings;
-use crate::DioxusCrate;
-use crate::{build::BuildArgs, PackageType};
+use crate::{AppBundle, BuildArgs, Builder, DioxusCrate, Platform};
 use anyhow::Context;
-use std::env::current_dir;
-use std::str::FromStr;
-use tauri_bundler::{PackageSettings, SettingsBuilder};
+use std::collections::HashMap;
+use tauri_bundler::{BundleBinary, BundleSettings, PackageSettings, SettingsBuilder};
 
 use super::*;
 
 /// Bundle the Rust desktop app and all of its assets
 #[derive(Clone, Debug, Parser)]
-#[clap(name = "bundle")]
 pub struct Bundle {
     /// The package types to bundle
+    ///
+    /// Any of:
+    /// - macos: The macOS application bundle (.app).
+    /// - ios: The iOS app bundle.
+    /// - msi: The Windows bundle (.msi).
+    /// - nsis: The NSIS bundle (.exe).
+    /// - deb: The Linux Debian package bundle (.deb).
+    /// - rpm: The Linux RPM bundle (.rpm).
+    /// - appimage: The Linux AppImage bundle (.AppImage).
+    /// - dmg: The macOS DMG bundle (.dmg).
+    /// - updater: The Updater bundle.
     #[clap(long)]
-    pub packages: Option<Vec<PackageType>>,
+    pub package_types: Option<Vec<crate::PackageType>>,
+
+    /// The directory in which the final bundle will be placed.
+    ///
+    /// Relative paths will be placed relative to the current working directory.
+    ///
+    /// We will flatten the artifacts into this directory - there will be no differentiation between
+    /// artifacts produced by different platforms.
+    #[clap(long)]
+    pub outdir: Option<PathBuf>,
 
     /// The arguments for the dioxus build
     #[clap(flatten)]
@@ -22,31 +38,124 @@ pub struct Bundle {
 }
 
 impl Bundle {
-    pub(crate) async fn bundle(mut self) -> anyhow::Result<()> {
+    pub(crate) async fn bundle(mut self) -> Result<StructuredOutput> {
+        tracing::info!("Bundling project...");
+
         let krate = DioxusCrate::new(&self.build_arguments.target_args)
             .context("Failed to load Dioxus workspace")?;
 
+        // We always use `release` mode for bundling
+        self.build_arguments.release = true;
         self.build_arguments.resolve(&krate)?;
 
-        // Build the app
-        let bundle = self.build_arguments.build().await?;
+        tracing::info!("Building app...");
 
-        // copy the binary to the out dir
-        let package = krate.package();
+        let bundle = Builder::start(&krate, self.build_arguments.clone())?
+            .finish()
+            .await?;
+
+        tracing::info!("Copying app to output directory...");
+
+        // If we're building for iOS, we need to bundle the iOS bundle
+        if self.build_arguments.platform() == Platform::Ios && self.package_types.is_none() {
+            self.package_types = Some(vec![crate::PackageType::IosBundle]);
+        }
+
+        let mut cmd_result = StructuredOutput::Success;
+
+        match self.build_arguments.platform() {
+            // By default, mac/win/linux work with tauri bundle
+            Platform::MacOS | Platform::Linux | Platform::Windows => {
+                let bundles = self.bundle_desktop(krate, bundle)?;
+
+                tracing::info!("Bundled app successfully!");
+                tracing::info!("App produced {} outputs:", bundles.len());
+                tracing::debug!("Bundling produced bundles: {:#?}", bundles);
+
+                // Copy the bundles to the output directory and log their locations
+                let mut bundle_paths = vec![];
+                for bundle in bundles {
+                    for src in bundle.bundle_paths {
+                        let src = if let Some(outdir) = &self.outdir {
+                            let dest = outdir.join(src.file_name().unwrap());
+                            crate::fastfs::copy_asset(&src, &dest)?;
+                            dest
+                        } else {
+                            src.clone()
+                        };
+
+                        tracing::info!(
+                            "{} - [{}]",
+                            bundle.package_type.short_name(),
+                            src.display()
+                        );
+
+                        bundle_paths.push(src);
+                    }
+                }
+
+                cmd_result = StructuredOutput::BundleOutput {
+                    bundles: bundle_paths,
+                };
+            }
+
+            Platform::Web => {
+                tracing::info!("App available at: {}", bundle.build.root_dir().display());
+            }
+
+            Platform::Ios => {
+                tracing::warn!("Signed iOS bundles are not yet supported");
+                tracing::info!(
+                    "The bundle is available at: {}",
+                    bundle.build.root_dir().display()
+                );
+            }
+
+            Platform::Server => {
+                tracing::info!("Server available at: {}", bundle.build.root_dir().display())
+            }
+            Platform::Liveview => tracing::info!(
+                "Liveview server available at: {}",
+                bundle.build.root_dir().display()
+            ),
+
+            Platform::Android => {
+                return Err(Error::UnsupportedFeature(
+                    "Android bundles are not yet supported".into(),
+                ));
+            }
+        };
 
+        Ok(cmd_result)
+    }
+
+    fn bundle_desktop(
+        &self,
+        krate: DioxusCrate,
+        bundle: AppBundle,
+    ) -> Result<Vec<tauri_bundler::Bundle>, Error> {
+        _ = std::fs::remove_dir_all(krate.bundle_dir(self.build_arguments.platform()));
+
+        let package = krate.package();
         let mut name: PathBuf = krate.executable_name().into();
         if cfg!(windows) {
             name.set_extension("exe");
         }
+        std::fs::create_dir_all(krate.bundle_dir(self.build_arguments.platform()))?;
+        std::fs::copy(
+            &bundle.app.exe,
+            krate
+                .bundle_dir(self.build_arguments.platform())
+                .join(krate.executable_name()),
+        )?;
 
-        // bundle the app
         let binaries = vec![
-            tauri_bundler::BundleBinary::new(name.display().to_string(), true)
-                .set_src_path(Some(krate.workspace_dir().display().to_string())),
+            // We use the name of the exe but it has to be in the same directory
+            BundleBinary::new(name.display().to_string(), true)
+                .set_src_path(Some(bundle.app.exe.display().to_string())),
         ];
 
-        let bundle_config = krate.config.bundle.clone();
-        let mut bundle_settings = make_tauri_bundler_settings(bundle_config);
+        let mut bundle_settings: BundleSettings = krate.config.bundle.clone().into();
 
         if cfg!(windows) {
             let windows_icon_override = krate.config.bundle.windows.as_ref().map(|w| &w.icon_path);
@@ -62,38 +171,28 @@ impl Bundle {
             }
         }
 
-        // Don't copy the executable or the old bundle directory
-        let ignored_files = [krate
-            .bundle_dir(self.build_arguments.platform())
-            .join("bundle")];
+        if bundle_settings.resources_map.is_none() {
+            bundle_settings.resources_map = Some(HashMap::new());
+        }
 
-        for entry in std::fs::read_dir(bundle.asset_dir())?.flatten() {
-            let path = entry.path().canonicalize()?;
-            if ignored_files.iter().any(|f| path.starts_with(f)) {
-                continue;
-            }
+        for entry in std::fs::read_dir(bundle.build.asset_dir())?.flatten() {
+            let old = entry.path().canonicalize()?;
+            let new = PathBuf::from("assets").join(old.file_name().unwrap());
+            tracing::debug!("Bundled asset: {old:?} -> {new:?}");
 
-            // Tauri bundle will add a __root__ prefix if the input path is absolute even though the output path is relative?
-            // We strip the prefix here to make sure the input path is relative so that the bundler puts the output path in the right place
-            let path = path
-                .strip_prefix(&current_dir()?)
-                .unwrap()
-                .display()
-                .to_string();
-            if let Some(resources) = &mut bundle_settings.resources_map {
-                resources.insert(path, "".to_string());
-            } else {
-                bundle_settings.resources_map = Some([(path, "".to_string())].into());
-            }
+            bundle_settings
+                .resources_map
+                .as_mut()
+                .expect("to be set")
+                .insert(old.display().to_string(), new.display().to_string());
         }
 
-        // Drain any resources set in the config into the resources map. Tauri bundle doesn't let you set both resources and resources_map https://github.com/DioxusLabs/dioxus/issues/2941
         for resource_path in bundle_settings.resources.take().into_iter().flatten() {
-            if let Some(resources) = &mut bundle_settings.resources_map {
-                resources.insert(resource_path, "".to_string());
-            } else {
-                bundle_settings.resources_map = Some([(resource_path, "".to_string())].into());
-            }
+            bundle_settings
+                .resources_map
+                .as_mut()
+                .expect("to be set")
+                .insert(resource_path, "".to_string());
         }
 
         let mut settings = SettingsBuilder::new()
@@ -106,9 +205,11 @@ impl Bundle {
                 authors: Some(package.authors.clone()),
                 default_run: Some(krate.executable_name().to_string()),
             })
+            .log_level(log::Level::Debug)
             .binaries(binaries)
             .bundle_settings(bundle_settings);
-        if let Some(packages) = &self.packages {
+
+        if let Some(packages) = &self.package_types {
             settings = settings.package_types(packages.iter().map(|p| (*p).into()).collect());
         }
 
@@ -116,37 +217,23 @@ impl Bundle {
             settings = settings.target(target.to_string());
         }
 
-        let settings = settings.build();
-
-        // on macos we need to set CI=true (https://github.com/tauri-apps/tauri/issues/2567)
-        #[cfg(target_os = "macos")]
-        std::env::set_var("CI", "true");
+        if self.build_arguments.platform() == Platform::Ios {
+            settings = settings.target("aarch64-apple-ios".to_string());
+        }
 
-        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"))]
-            panic!("Failed to bundle project: {:#?}", err);
-        });
+        let settings = settings.build()?;
+        tracing::debug!("Bundling project with settings: {:#?}", settings);
+        if cfg!(target_os = "macos") {
+            std::env::set_var("CI", "true");
+        }
 
-        Ok(())
-    }
-}
+        let bundles = tauri_bundler::bundle::bundle_project(&settings).inspect_err(|err| {
+            tracing::error!("Failed to bundle project: {:#?}", err);
+            if cfg!(target_os = "macos") {
+                tracing::error!("Make 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)");
+            }
+        })?;
 
-impl FromStr for PackageType {
-    type Err = String;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "macos" => Ok(PackageType::MacOsBundle),
-            "ios" => Ok(PackageType::IosBundle),
-            "msi" => Ok(PackageType::WindowsMsi),
-            "deb" => Ok(PackageType::Deb),
-            "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)),
-        }
+        Ok(bundles)
     }
 }

+ 23 - 25
packages/cli/src/cli/check.rs

@@ -1,10 +1,13 @@
+//! Run linting against the user's codebase.
+//!
+//! For reference, the rustfmt main.rs file
+//! https://github.com/rust-lang/rustfmt/blob/master/src/bin/main.rs
+
 use super::*;
 use crate::DioxusCrate;
+use anyhow::Context;
 use futures_util::{stream::FuturesUnordered, StreamExt};
-use std::{path::Path, process::exit};
-
-// For reference, the rustfmt main.rs file
-// https://github.com/rust-lang/rustfmt/blob/master/src/bin/main.rs
+use std::path::Path;
 
 /// Check the Rust files in the project for issues.
 #[derive(Clone, Debug, Parser)]
@@ -20,25 +23,23 @@ pub(crate) struct Check {
 
 impl Check {
     // Todo: check the entire crate
-    pub(crate) async fn check(self) -> Result<()> {
+    pub(crate) async fn check(self) -> Result<StructuredOutput> {
         match self.file {
             // Default to checking the project
             None => {
                 let dioxus_crate = DioxusCrate::new(&self.target_args)?;
-                if let Err(e) = check_project_and_report(dioxus_crate).await {
-                    eprintln!("error checking project: {}", e);
-                    exit(1);
-                }
+                check_project_and_report(dioxus_crate)
+                    .await
+                    .context("error checking project")?;
             }
             Some(file) => {
-                if let Err(e) = check_file_and_report(file).await {
-                    eprintln!("failed to check file: {}", e);
-                    exit(1);
-                }
+                check_file_and_report(file)
+                    .await
+                    .context("error checking file")?;
             }
         }
 
-        Ok(())
+        Ok(StructuredOutput::Success)
     }
 }
 
@@ -72,7 +73,7 @@ async fn check_files_and_report(files_to_check: Vec<PathBuf>) -> Result<()> {
             .await;
 
             if res.is_err() {
-                eprintln!("error checking file: {}", path.display());
+                tracing::error!("error checking file: {}", path.display());
             }
 
             res
@@ -92,19 +93,17 @@ async fn check_files_and_report(files_to_check: Vec<PathBuf>) -> Result<()> {
 
     for report in issue_reports.into_iter() {
         if !report.issues.is_empty() {
-            println!("{}", report);
+            tracing::info!("{}", report);
         }
     }
 
     match total_issues {
-        0 => println!("No issues found."),
-        1 => println!("1 issue found."),
-        _ => println!("{} issues found.", total_issues),
-    }
-
-    match total_issues {
-        0 => exit(0),
-        _ => exit(1),
+        0 => {
+            tracing::info!("No issues found.");
+            Ok(())
+        }
+        1 => Err("1 issue found.".into()),
+        _ => Err(format!("{} issues found.", total_issues).into()),
     }
 }
 
@@ -114,7 +113,6 @@ fn collect_rs_files(folder: &Path, files: &mut Vec<PathBuf>) {
     };
 
     // load the gitignore
-
     for entry in folder {
         let Ok(entry) = entry else {
             continue;

+ 7 - 6
packages/cli/src/cli/clean.rs

@@ -1,25 +1,26 @@
 use super::*;
+use crate::Result;
 
 /// Clean build artifacts.
 ///
 /// Simlpy runs `cargo clean`
 #[derive(Clone, Debug, Parser)]
-#[clap(name = "clean")]
 pub(crate) struct Clean {}
 
 impl Clean {
     /// todo(jon): we should add a config option that just wipes target/dx and target/dioxus-client instead of doing a full clean
-    pub(crate) fn clean(self) -> anyhow::Result<()> {
-        let output = Command::new("cargo")
+    pub(crate) async fn clean(self) -> Result<StructuredOutput> {
+        let output = tokio::process::Command::new("cargo")
             .arg("clean")
             .stdout(Stdio::piped())
             .stderr(Stdio::piped())
-            .output()?;
+            .output()
+            .await?;
 
         if !output.status.success() {
-            return Err(anyhow::anyhow!("Cargo clean failed."));
+            return Err(anyhow::anyhow!("Cargo clean failed.").into());
         }
 
-        Ok(())
+        Ok(StructuredOutput::Success)
     }
 }

+ 6 - 6
packages/cli/src/cli/config.rs

@@ -4,7 +4,6 @@ use crate::{metadata::crate_root, CliSettings};
 
 /// Dioxus config file controls
 #[derive(Clone, Debug, Deserialize, Subcommand)]
-#[clap(name = "config")]
 pub(crate) enum Config {
     /// Init `Dioxus.toml` for project/folder.
     Init {
@@ -76,7 +75,7 @@ impl From<BoolValue> for bool {
 }
 
 impl Config {
-    pub(crate) fn config(self) -> Result<()> {
+    pub(crate) fn config(self) -> Result<StructuredOutput> {
         let crate_root = crate_root()?;
         match self {
             Config::Init {
@@ -89,7 +88,7 @@ impl Config {
                     tracing::warn!(
                         "config file `Dioxus.toml` already exist, use `--force` to overwrite it."
                     );
-                    return Ok(());
+                    return Ok(StructuredOutput::Success);
                 }
                 let mut file = File::create(conf_path)?;
                 let content = String::from(include_str!("../../assets/dioxus.toml"))
@@ -99,7 +98,7 @@ impl Config {
                 tracing::info!(dx_src = ?TraceSrc::Dev, "🚩 Init config file completed.");
             }
             Config::FormatPrint {} => {
-                println!(
+                tracing::info!(
                     "{:#?}",
                     crate::dioxus_crate::DioxusCrate::new(&TargetArgs::default())?.config
                 );
@@ -112,7 +111,7 @@ impl Config {
                 tracing::info!(dx_src = ?TraceSrc::Dev, "🚩 Create custom html file done.");
             }
             Config::LogFile {} => {
-                let log_path = crate::tracer::log_path();
+                let log_path = crate::logging::FileAppendLayer::log_path();
                 tracing::info!(dx_src = ?TraceSrc::Dev, "Log file is located at {}", log_path.display());
             }
             // Handle CLI settings.
@@ -132,6 +131,7 @@ impl Config {
                 tracing::info!(dx_src = ?TraceSrc::Dev, "🚩 CLI setting `{setting}` has been set.");
             }
         }
-        Ok(())
+
+        Ok(StructuredOutput::Success)
     }
 }

+ 4 - 3
packages/cli/src/cli/create.rs

@@ -7,7 +7,6 @@ use std::path::Path;
 pub(crate) static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template";
 
 #[derive(Clone, Debug, Default, Deserialize, Parser)]
-#[clap(name = "new")]
 pub(crate) struct Create {
     /// Project name (required when `--yes` is used)
     name: Option<String>,
@@ -39,7 +38,7 @@ pub(crate) struct Create {
 }
 
 impl Create {
-    pub(crate) fn create(mut self) -> Result<()> {
+    pub(crate) fn create(mut self) -> Result<StructuredOutput> {
         let metadata = cargo_metadata::MetadataCommand::new().exec().ok();
 
         // If we're getting pass a `.` name, that's actually a path
@@ -106,7 +105,9 @@ impl Create {
         .expect("ctrlc::set_handler");
         let path = cargo_generate::generate(args)?;
 
-        post_create(&path, metadata)
+        post_create(&path, metadata)?;
+
+        Ok(StructuredOutput::Success)
     }
 }
 

+ 3 - 2
packages/cli/src/cli/doctor.rs

@@ -1,10 +1,11 @@
+use crate::{Result, StructuredOutput};
 use clap::Parser;
 
 #[derive(Clone, Debug, Parser)]
 pub struct Doctor {}
 
 impl Doctor {
-    pub async fn run(self) -> anyhow::Result<()> {
-        Ok(())
+    pub async fn run(self) -> Result<StructuredOutput> {
+        Ok(StructuredOutput::Success)
     }
 }

+ 7 - 6
packages/cli/src/cli/init.rs

@@ -3,28 +3,27 @@ use crate::cli::create::DEFAULT_TEMPLATE;
 use cargo_generate::{GenerateArgs, TemplatePath};
 
 #[derive(Clone, Debug, Default, Deserialize, Parser)]
-#[clap(name = "init")]
 pub(crate) struct Init {
     /// Template path
     #[clap(default_value = DEFAULT_TEMPLATE, short, long)]
     template: String,
+
     /// Pass <option>=<value> for the used template (e.g., `foo=bar`)
     #[clap(short, long)]
     option: Vec<String>,
+
     /// Specify a sub-template within the template repository to be used as the actual template
     #[clap(long)]
     subtemplate: Option<String>,
+
     /// Skip user interaction by using the default values for the used template.
     /// Default values can be overridden with `--option`
     #[clap(short, long)]
     yes: bool,
-    // TODO: turn on/off cargo-generate's output (now is invisible)
-    // #[clap(default_value = "false", short, long)]
-    // silent: bool,
 }
 
 impl Init {
-    pub(crate) fn init(self) -> Result<()> {
+    pub(crate) fn init(self) -> Result<StructuredOutput> {
         let metadata = cargo_metadata::MetadataCommand::new().exec().ok();
 
         // Get directory name.
@@ -57,6 +56,8 @@ impl Init {
             ..Default::default()
         };
         let path = cargo_generate::generate(args)?;
-        create::post_create(&path, metadata)
+        create::post_create(&path, metadata)?;
+
+        Ok(StructuredOutput::Success)
     }
 }

+ 3 - 6
packages/cli/src/cli/link.rs

@@ -1,8 +1,7 @@
+use crate::assets::AssetManifest;
 use serde::{Deserialize, Serialize};
 use std::path::PathBuf;
 
-use crate::assets::AssetManifest;
-
 #[derive(Debug, Serialize, Deserialize)]
 pub enum LinkAction {
     BuildAssetManifest {
@@ -15,7 +14,7 @@ pub enum LinkAction {
 }
 
 impl LinkAction {
-    pub(crate) const ENV_VAR_NAME: &'static str = "dx-magic-link-file";
+    pub(crate) const ENV_VAR_NAME: &'static str = "dx_magic_link_file";
 
     /// Should we write the input arguments to a file (aka act as a linker subprocess)?
     ///
@@ -39,7 +38,7 @@ impl LinkAction {
     ///
     /// 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 run(self) -> anyhow::Result<()> {
+    pub(crate) fn run(self) {
         match self {
             // Literally just run the android linker :)
             LinkAction::LinkAndroid {
@@ -102,7 +101,5 @@ impl LinkAction {
                 std::fs::write(dest, contents).expect("Failed to write output file");
             }
         }
-
-        Ok(())
     }
 }

+ 16 - 11
packages/cli/src/cli/mod.rs

@@ -12,12 +12,14 @@ pub(crate) mod run;
 pub(crate) mod serve;
 pub(crate) mod target;
 pub(crate) mod translate;
+pub(crate) mod verbosity;
 
 pub(crate) use build::*;
 pub(crate) use serve::*;
 pub(crate) use target::*;
+pub(crate) use verbosity::*;
 
-use crate::{error::Result, Error};
+use crate::{error::Result, Error, StructuredOutput};
 use anyhow::Context;
 use clap::{Parser, Subcommand};
 use html_parser::Dom;
@@ -35,40 +37,42 @@ use std::{
 #[derive(Parser)]
 #[clap(name = "dioxus", version = VERSION.as_str())]
 pub(crate) struct Cli {
-    #[clap(subcommand)]
+    #[command(subcommand)]
     pub(crate) action: Commands,
 
-    /// Enable verbose logging.
-    #[clap(short)]
-    pub(crate) v: bool,
-
-    /// Specify a binary target.
-    #[clap(global = true, long)]
-    pub(crate) bin: Option<String>,
+    #[command(flatten)]
+    pub(crate) verbosity: Verbosity,
 }
 
-#[derive(Parser)]
+#[derive(Subcommand)]
 pub(crate) enum Commands {
     /// Build the Dioxus project and all of its assets.
+    #[clap(name = "build")]
     Build(build::BuildArgs),
 
     /// Translate a source file into Dioxus code.
+    #[clap(name = "translate")]
     Translate(translate::Translate),
 
     /// Build, watch & serve the Dioxus project and all of its assets.
+    #[clap(name = "serve")]
     Serve(serve::ServeArgs),
 
     /// Create a new project for Dioxus.
+    #[clap(name = "new")]
     New(create::Create),
 
     /// Init a new project for Dioxus in an existing directory.
     /// Will attempt to keep your project in a good state.
+    #[clap(name = "init")]
     Init(init::Init),
 
     /// Clean output artifacts.
+    #[clap(name = "clean")]
     Clean(clean::Clean),
 
     /// Bundle the Dioxus app into a shippable object.
+    #[clap(name = "bundle")]
     Bundle(bundle::Bundle),
 
     /// Automatically format RSX.
@@ -89,6 +93,7 @@ pub(crate) enum Commands {
 
     /// Dioxus config file controls.
     #[clap(subcommand)]
+    #[clap(name = "config")]
     Config(config::Config),
 }
 
@@ -111,7 +116,7 @@ impl Display for Commands {
     }
 }
 
-pub(crate) static VERSION: Lazy<String> = Lazy::new(|| {
+static VERSION: Lazy<String> = Lazy::new(|| {
     format!(
         "{} ({})",
         crate::dx_build_info::PKG_VERSION,

+ 12 - 8
packages/cli/src/cli/run.rs

@@ -1,5 +1,5 @@
 use super::*;
-use crate::{serve::ServeUpdate, BuildArgs, Builder, DioxusCrate};
+use crate::{serve::ServeUpdate, BuildArgs, Builder, DioxusCrate, Result};
 
 /// Run the project with the given arguments
 #[derive(Clone, Debug, Parser)]
@@ -10,14 +10,14 @@ pub(crate) struct RunArgs {
 }
 
 impl RunArgs {
-    pub(crate) async fn run(mut self) -> anyhow::Result<()> {
+    pub(crate) async fn run(mut self) -> Result<StructuredOutput> {
         let krate = DioxusCrate::new(&self.build_args.target_args)
             .context("Failed to load Dioxus workspace")?;
 
         self.build_args.resolve(&krate)?;
 
-        println!("Building crate krate data: {:#?}", krate);
-        println!("Build args: {:#?}", self.build_args);
+        tracing::trace!("Building crate krate data: {:#?}", krate);
+        tracing::trace!("Build args: {:#?}", self.build_args);
 
         let bundle = Builder::start(&krate, self.build_args.clone())?
             .finish()
@@ -35,11 +35,15 @@ impl RunArgs {
         // They won't generally be emitted
         loop {
             match runner.wait().await {
-                ServeUpdate::StderrReceived { platform, msg } => println!("[{platform}]: {msg}"),
-                ServeUpdate::StdoutReceived { platform, msg } => println!("[{platform}]: {msg}"),
+                ServeUpdate::StderrReceived { platform, msg } => {
+                    tracing::info!("[{platform}]: {msg}")
+                }
+                ServeUpdate::StdoutReceived { platform, msg } => {
+                    tracing::info!("[{platform}]: {msg}")
+                }
                 ServeUpdate::ProcessExited { platform, status } => {
                     runner.kill(platform);
-                    eprintln!("[{platform}]: process exited with status: {status:?}");
+                    tracing::info!("[{platform}]: process exited with status: {status:?}");
                     break;
                 }
                 ServeUpdate::BuildUpdate { .. } => {}
@@ -55,6 +59,6 @@ impl RunArgs {
             }
         }
 
-        Ok(())
+        Ok(StructuredOutput::Success)
     }
 }

+ 17 - 7
packages/cli/src/cli/serve.rs

@@ -1,11 +1,9 @@
 use super::*;
 use crate::{AddressArguments, BuildArgs, DioxusCrate, Platform};
-use anyhow::Context;
 
 /// Serve the project
 #[derive(Clone, Debug, Default, Parser)]
 #[command(group = clap::ArgGroup::new("release-incompatible").multiple(true).conflicts_with("release"))]
-#[clap(name = "serve")]
 pub(crate) struct ServeArgs {
     /// The arguments for the address the server will run on
     #[clap(flatten)]
@@ -47,10 +45,22 @@ pub(crate) struct ServeArgs {
 
 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 krate = DioxusCrate::new(&self.build_arguments.target_args)
-            .context("Failed to load Dioxus workspace")?;
+    ///
+    /// Make sure not to do any intermediate logging since our tracing infra has now enabled much
+    /// higher log levels
+    pub(crate) async fn serve(self) -> Result<StructuredOutput> {
+        crate::serve::serve_all(self).await?;
 
+        Ok(StructuredOutput::Success)
+    }
+
+    pub(crate) fn load_krate(&mut self) -> Result<DioxusCrate> {
+        let krate = DioxusCrate::new(&self.build_arguments.target_args)?;
+        self.resolve(&krate)?;
+        Ok(krate)
+    }
+
+    pub(crate) fn resolve(&mut self, krate: &DioxusCrate) -> Result<()> {
         // Enable hot reload.
         if self.hot_reload.is_none() {
             self.hot_reload = Some(krate.settings.always_hot_reload.unwrap_or(true));
@@ -72,9 +82,9 @@ impl ServeArgs {
         }
 
         // Resolve the build arguments
-        self.build_arguments.resolve(&krate)?;
+        self.build_arguments.resolve(krate)?;
 
-        crate::serve::serve_all(self, krate).await
+        Ok(())
     }
 
     pub(crate) fn should_hotreload(&self) -> bool {

+ 12 - 10
packages/cli/src/cli/translate.rs

@@ -1,11 +1,9 @@
 use super::*;
-use crate::{Result, TraceSrc};
+use crate::{Result, StructuredOutput};
 use dioxus_rsx::{BodyNode, CallBody, TemplateBody};
-use std::{io::IsTerminal as _, process::exit};
 
 /// Translate some source file into Dioxus code
 #[derive(Clone, Debug, Parser)]
-#[clap(name = "translate")]
 pub(crate) struct Translate {
     /// Activate debug mode
     // short and long flags (-d, --debug) will be deduced from the field's name
@@ -26,7 +24,7 @@ pub(crate) struct Translate {
 }
 
 impl Translate {
-    pub(crate) fn translate(self) -> Result<()> {
+    pub(crate) fn translate(self) -> Result<StructuredOutput> {
         // Get the right input for the translation
         let contents = determine_input(self.file, self.raw)?;
 
@@ -34,15 +32,18 @@ impl Translate {
         let dom = html_parser::Dom::parse(&contents)?;
 
         // Convert the HTML to RSX
-        let out = convert_html_to_formatted_rsx(&dom, self.component);
+        let html = convert_html_to_formatted_rsx(&dom, self.component);
 
         // Write the output
+        // todo(jon): we should probably use tracing out a different output format
+        // right now we're just printing to stdout since some tools rely on that, but likely we don't want that
+        // instead we should be printing as json (or maybe even a different format) if we're not interactive
         match self.output {
-            Some(output) => std::fs::write(output, out)?,
-            None => print!("{}", out),
+            Some(output) => std::fs::write(output, &html)?,
+            None => print!("{}", html),
         }
 
-        Ok(())
+        Ok(StructuredOutput::HtmlTranslate { html })
     }
 }
 
@@ -103,10 +104,11 @@ fn indent_and_write(raw: &str, idx: usize, out: &mut String) {
 }
 
 fn determine_input(file: Option<String>, raw: Option<String>) -> Result<String> {
+    use std::io::IsTerminal as _;
+
     // Make sure not both are specified
     if file.is_some() && raw.is_some() {
-        tracing::error!(dx_src = ?TraceSrc::Dev, "Only one of --file or --raw should be specified.");
-        exit(0);
+        return Err("Only one of --file or --raw should be specified.".into());
     }
 
     if let Some(raw) = raw {

+ 16 - 0
packages/cli/src/cli/verbosity.rs

@@ -0,0 +1,16 @@
+use clap::Parser;
+
+#[derive(Parser, Clone, Copy, Debug)]
+pub struct Verbosity {
+    /// Use verbose output [default: false]
+    #[clap(long, global = true)]
+    pub(crate) verbose: bool,
+
+    /// Use trace output [default: false]
+    #[clap(long, global = true)]
+    pub(crate) trace: bool,
+
+    /// Output logs in JSON format
+    #[clap(long, global = true)]
+    pub(crate) json_output: bool,
+}

+ 29 - 9
packages/cli/src/config/bundle.rs

@@ -1,6 +1,6 @@
 use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
 use std::path::PathBuf;
+use std::{collections::HashMap, str::FromStr};
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
 pub(crate) struct BundleConfig {
@@ -181,11 +181,22 @@ impl Default for WebviewInstallMode {
     }
 }
 
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct CustomSignCommandSettings {
+    /// The command to run to sign the binary.
+    pub cmd: String,
+    /// The arguments to pass to the command.
+    ///
+    /// "%1" will be replaced with the path to the binary to be signed.
+    pub args: Vec<String>,
+}
+
 #[derive(Clone, Copy, Debug)]
 pub(crate) enum PackageType {
     MacOsBundle,
     IosBundle,
     WindowsMsi,
+    Nsis,
     Deb,
     Rpm,
     AppImage,
@@ -193,12 +204,21 @@ pub(crate) enum PackageType {
     Updater,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct CustomSignCommandSettings {
-    /// The command to run to sign the binary.
-    pub cmd: String,
-    /// The arguments to pass to the command.
-    ///
-    /// "%1" will be replaced with the path to the binary to be signed.
-    pub args: Vec<String>,
+impl FromStr for PackageType {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "macos" => Ok(PackageType::MacOsBundle),
+            "ios" => Ok(PackageType::IosBundle),
+            "msi" => Ok(PackageType::WindowsMsi),
+            "nsis" => Ok(PackageType::Nsis),
+            "deb" => Ok(PackageType::Deb),
+            "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)),
+        }
+    }
 }

+ 63 - 54
packages/cli/src/dioxus_crate.rs

@@ -25,14 +25,15 @@ pub(crate) static PROFILE_SERVER: &str = "dioxus-server";
 
 impl DioxusCrate {
     pub(crate) fn new(target: &TargetArgs) -> Result<Self> {
-        let mut cmd = Cmd::new();
-        cmd.features(target.features.clone());
-
-        let krates = krates::Builder::new()
+        tracing::debug!("Loading crate");
+        let cmd = Cmd::new();
+        let builder = krates::Builder::new();
+        let krates = builder
             .build(cmd, |_| {})
             .context("Failed to run cargo metadata")?;
 
         let package = find_main_package(&krates, target.package.clone())?;
+        tracing::debug!("Found package {package:?}");
 
         let dioxus_config = DioxusConfig::load(&krates, package)?.unwrap_or_default();
 
@@ -173,6 +174,7 @@ impl DioxusCrate {
             .get_enabled_features(krate.kid)?
             .iter()
             .flat_map(|feature| {
+                tracing::trace!("Autodetecting platform from feature {feature}");
                 Platform::autodetect_from_cargo_feature(feature).map(|f| (f, feature.to_string()))
             })
             .collect::<Vec<_>>();
@@ -230,8 +232,6 @@ impl DioxusCrate {
             return possible_platforms.first().cloned();
         }
 
-        tracing::warn!("Could not autodetect platform. Platform must be explicitly specified. Pass `--platform <platform>` or set a default platform using a cargo feature.");
-
         None
     }
 
@@ -246,13 +246,13 @@ impl DioxusCrate {
     }
 
     /// Get the features required to build for the given platform
-    pub(crate) fn feature_for_platform(&self, platform: Platform) -> Option<String> {
+    pub(crate) fn feature_for_platform(&self, platform: Platform) -> String {
         let package = self.package();
 
         // Try to find the feature that activates the dioxus feature for the given platform
         let dioxus_feature = platform.feature_name();
 
-        package.features.iter().find_map(|(key, features)| {
+        let res = package.features.iter().find_map(|(key, features)| {
             // if the feature is just the name of the platform, we use that
             if key == dioxus_feature {
                 return Some(key.clone());
@@ -271,7 +271,16 @@ impl DioxusCrate {
                     }
                 }
             }
+
             None
+        });
+
+        res.unwrap_or_else(|| {
+            let fallback = format!("dioxus/{}", platform.feature_name()) ;
+            tracing::debug!(
+                "Could not find explicit feature for platform {platform}, passing `fallback` instead"
+            );
+            fallback
         })
     }
 
@@ -312,14 +321,13 @@ impl DioxusCrate {
             if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_SERVER) {
                 let mut server = toml_edit::Table::new();
                 server.insert("inherits", Item::Value("dev".into()));
-                server.insert("opt-level", Item::Value(2.into()));
+                // server.insert("opt-level", Item::Value(2.into()));
                 entry.insert(Item::Table(server));
             }
 
             if let toml_edit::Entry::Vacant(entry) = table.entry(PROFILE_ANDROID) {
                 let mut android = toml_edit::Table::new();
                 android.insert("inherits", Item::Value("dev".into()));
-                android.insert("opt-level", Item::Value(2.into()));
                 entry.insert(Item::Table(android));
             }
         }
@@ -561,59 +569,60 @@ impl std::fmt::Debug for DioxusCrate {
 
 // Find the main package in the workspace
 fn find_main_package(krates: &Krates, package: Option<String>) -> Result<NodeId> {
-    let kid = match package {
-        Some(package) => {
-            let mut workspace_members = krates.workspace_members();
-            let found = workspace_members.find_map(|node| {
-                if let krates::Node::Krate { id, krate, .. } = node {
-                    if krate.name == package {
-                        return Some(id);
-                    }
+    if let Some(package) = package {
+        let mut workspace_members = krates.workspace_members();
+        let found = workspace_members.find_map(|node| {
+            if let krates::Node::Krate { id, krate, .. } = node {
+                if krate.name == package {
+                    return Some(id);
                 }
-                None
-            });
-
-            if found.is_none() {
-                eprintln!("Could not find package {package} in the workspace. Did you forget to add it to the workspace?");
-                eprintln!("Packages in the workspace:");
-                for package in krates.workspace_members() {
-                    if let krates::Node::Krate { krate, .. } = package {
-                        eprintln!("{}", krate.name());
-                    }
+            }
+            None
+        });
+
+        if found.is_none() {
+            tracing::error!("Could not find package {package} in the workspace. Did you forget to add it to the workspace?");
+            tracing::error!("Packages in the workspace:");
+            for package in krates.workspace_members() {
+                if let krates::Node::Krate { krate, .. } = package {
+                    tracing::error!("{}", krate.name());
                 }
             }
-
-            found.ok_or_else(|| anyhow::anyhow!("Failed to find package {package}"))?
         }
-        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));
-                            }
+
+        let kid = found.ok_or_else(|| anyhow::anyhow!("Failed to find package {package}"))?;
+
+        return Ok(krates.nid_for_kid(kid).unwrap());
+    };
+
+    // 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)
-                .context("Failed to find current package")?
         }
-    };
+    }
+
+    let kid = closest_parent
+        .map(|(id, _)| id)
+        .context("Failed to find current package")?;
 
     let package = krates.nid_for_kid(kid).unwrap();
     Ok(package)

+ 9 - 0
packages/cli/src/error.rs

@@ -30,6 +30,15 @@ pub(crate) enum Error {
     #[error("Failed to establish proxy: {0}")]
     ProxySetup(String),
 
+    #[error("Failed to bundle project: {0}")]
+    BundleFailed(#[from] tauri_bundler::Error),
+
+    #[error("Unsupported feature: {0}")]
+    UnsupportedFeature(String),
+
+    #[error("Failed to render template: {0}")]
+    TemplateParse(#[from] handlebars::RenderError),
+
     #[error(transparent)]
     Other(#[from] anyhow::Error),
 }

+ 125 - 95
packages/cli/src/tracer.rs → packages/cli/src/logging.rs

@@ -14,8 +14,9 @@
 //! 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::{serve::ServeUpdate, Platform as TargetPlatform};
+use crate::{serve::ServeUpdate, Cli, Commands, Platform as TargetPlatform, Verbosity};
 use cargo_metadata::{diagnostic::DiagnosticLevel, CompilerMessage};
+use clap::Parser;
 use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
 use once_cell::sync::OnceCell;
 use std::{
@@ -23,96 +24,125 @@ use std::{
     env,
     fmt::{Debug, Display, Write as _},
     fs,
-    io::{self, Write},
     path::PathBuf,
-    sync::{
-        atomic::{AtomicBool, Ordering},
-        Mutex,
-    },
+    sync::Mutex,
+    time::Instant,
 };
 use tracing::{field::Visit, Level, Subscriber};
-use tracing_subscriber::{fmt::format, prelude::*, registry::LookupSpan, EnvFilter, Layer};
+use tracing_subscriber::{
+    fmt::{
+        format::{self, Writer},
+        time::FormatTime,
+    },
+    prelude::*,
+    registry::LookupSpan,
+    EnvFilter, Layer,
+};
 
 const LOG_ENV: &str = "DIOXUS_LOG";
 const LOG_FILE_NAME: &str = "dx.log";
 const DX_SRC_FLAG: &str = "dx_src";
 
-pub fn log_path() -> PathBuf {
-    let tmp_dir = std::env::temp_dir();
-    tmp_dir.join(LOG_FILE_NAME)
-}
-
-static TUI_ENABLED: AtomicBool = AtomicBool::new(false);
 static TUI_TX: OnceCell<UnboundedSender<TraceMsg>> = OnceCell::new();
+pub static VERBOSITY: OnceCell<Verbosity> = OnceCell::new();
 
 pub(crate) struct TraceController {
     pub(crate) tui_rx: UnboundedReceiver<TraceMsg>,
 }
 
 impl TraceController {
-    /// Get a handle to the trace controller.
-    pub fn redirect() -> Self {
-        let (tui_tx, tui_rx) = unbounded();
-        TUI_ENABLED.store(true, Ordering::SeqCst);
-        TUI_TX.set(tui_tx.clone()).unwrap();
-        Self { tui_rx }
-    }
-
-    /// Wait for the internal logger to send a message
-    pub(crate) async fn wait(&mut self) -> ServeUpdate {
-        use futures_util::StreamExt;
-        let log = self.tui_rx.next().await.expect("tracer should never die");
-        ServeUpdate::TracingLog { log }
-    }
-
-    pub(crate) fn shutdown(&self) {
-        TUI_ENABLED.store(false, Ordering::SeqCst);
-    }
-
-    /// Build tracing infrastructure.
-    pub fn initialize() {
-        let mut filter =
-            EnvFilter::new("error,dx=trace,dioxus-cli=debug,manganis-cli-support=debug");
-
-        if env::var(LOG_ENV).is_ok() {
-            filter = EnvFilter::from_env(LOG_ENV);
+    /// Initialize the CLI and set up the tracing infrastructure
+    pub fn initialize() -> Cli {
+        let args = Cli::parse();
+
+        VERBOSITY
+            .set(args.verbosity)
+            .expect("verbosity should only be set once");
+
+        // When running in interactive mode (of which serve is the only one), we want to do things slightly differently
+        // This involves no fmt layer or file logging
+        if matches!(args.action, Commands::Serve(_)) {
+            Self::initialize_for_serve();
+            return args;
         }
 
-        // Log file
-        let log_path = log_path();
-        _ = std::fs::write(&log_path, "");
-        let file_append_layer = match FileAppendLayer::new(log_path) {
-            Ok(f) => Some(f),
-            Err(e) => {
-                tracing::error!(dx_src = ?TraceSrc::Dev, err = ?e, "failed to init log file");
-                None
-            }
+        // By default we capture ourselves at a higher tracing level when serving
+        // This ensures we're tracing ourselves even if we end up tossing the logs
+        let filter = if env::var(LOG_ENV).is_ok() {
+            EnvFilter::from_env(LOG_ENV)
+        } else {
+            EnvFilter::new(format!(
+                "error,dx={our_level},dioxus-cli={our_level},manganis-cli-support={our_level}",
+                our_level = if args.verbosity.verbose {
+                    "debug"
+                } else {
+                    "info"
+                }
+            ))
         };
 
-        // Build CLI layer
-        let cli_layer = CLILayer;
+        let json_filter = tracing_subscriber::filter::filter_fn(move |meta| {
+            if meta.fields().len() == 1 && meta.fields().iter().next().unwrap().name() == "json" {
+                return args.verbosity.json_output;
+            }
+            true
+        });
 
-        // Build fmt layer
         let fmt_layer = tracing_subscriber::fmt::layer()
+            .with_target(args.verbosity.verbose)
             .fmt_fields(
-                format::debug_fn(|writer, field, value| {
+                format::debug_fn(move |writer, field, value| {
+                    if field.name() == "json" && !args.verbosity.json_output {
+                        return Ok(());
+                    }
+
                     write!(writer, "{}", format_field(field.name(), value))
                 })
                 .delimited(" "),
             )
-            .with_writer(Mutex::new(FmtLogWriter {}))
-            .with_timer(tracing_subscriber::fmt::time::time());
+            .with_timer(PrettyUptime::default());
+
+        let fmt_layer = if args.verbosity.json_output {
+            fmt_layer.json().flatten_event(true).boxed()
+        } else {
+            fmt_layer.boxed()
+        };
 
         let sub = tracing_subscriber::registry()
             .with(filter)
-            .with(file_append_layer)
-            .with(cli_layer)
+            .with(json_filter)
+            .with(FileAppendLayer::new())
             .with(fmt_layer);
 
         #[cfg(feature = "tokio-console")]
         let sub = sub.with(console_subscriber::spawn());
 
         sub.init();
+
+        args
+    }
+
+    /// Get a handle to the trace controller.
+    pub fn redirect() -> Self {
+        let (tui_tx, tui_rx) = unbounded();
+        TUI_TX.set(tui_tx.clone()).unwrap();
+        Self { tui_rx }
+    }
+
+    /// Wait for the internal logger to send a message
+    pub(crate) async fn wait(&mut self) -> ServeUpdate {
+        use futures_util::StreamExt;
+        let log = self.tui_rx.next().await.expect("tracer should never die");
+        ServeUpdate::TracingLog { log }
+    }
+
+    fn initialize_for_serve() {
+        let filter = EnvFilter::new("error,dx=trace,dioxus-cli=trace,manganis-cli-support=trace");
+
+        tracing_subscriber::registry()
+            .with(filter)
+            .with(CLILayer {})
+            .init();
     }
 }
 
@@ -120,17 +150,27 @@ impl TraceController {
 ///
 /// This layer returns on any error allowing the cli to continue work
 /// despite failing to log to a file. This helps in case of permission errors and similar.
-struct FileAppendLayer {
+pub(crate) struct FileAppendLayer {
     file_path: PathBuf,
     buffer: Mutex<String>,
 }
 
 impl FileAppendLayer {
-    pub fn new(file_path: PathBuf) -> io::Result<Self> {
-        Ok(Self {
+    fn new() -> Self {
+        let file_path = Self::log_path();
+
+        if !file_path.exists() {
+            _ = std::fs::write(&file_path, "");
+        }
+
+        Self {
             file_path,
             buffer: Mutex::new(String::new()),
-        })
+        }
+    }
+
+    pub(crate) fn log_path() -> PathBuf {
+        std::env::temp_dir().join(LOG_FILE_NAME)
     }
 }
 
@@ -194,15 +234,6 @@ where
         let mut visitor = CollectVisitor::new();
         event.record(&mut visitor);
 
-        // If the TUI output is disabled we let fmt subscriber handle the logs
-        // EXCEPT for cargo logs which we just print.
-        if !TUI_ENABLED.load(Ordering::SeqCst) {
-            if visitor.source == TraceSrc::Cargo {
-                println!("{}", visitor.message);
-            }
-            return;
-        }
-
         let meta = event.metadata();
         let level = meta.level();
 
@@ -217,14 +248,11 @@ where
             visitor.source = TraceSrc::Dev;
         }
 
-        TUI_TX
+        _ = TUI_TX
             .get()
             .unwrap()
-            .unbounded_send(TraceMsg::text(visitor.source, *level, final_msg))
-            .unwrap();
+            .unbounded_send(TraceMsg::text(visitor.source, *level, final_msg));
     }
-
-    // TODO: support spans? structured tui log display?
 }
 
 /// A record visitor that collects dx-specific info and user-provided fields for logging consumption.
@@ -239,7 +267,6 @@ impl CollectVisitor {
         Self {
             message: String::new(),
             source: TraceSrc::Unknown,
-
             fields: HashMap::new(),
         }
     }
@@ -266,26 +293,6 @@ impl Visit for CollectVisitor {
     }
 }
 
-struct FmtLogWriter {}
-
-impl Write for FmtLogWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        if !TUI_ENABLED.load(Ordering::SeqCst) {
-            return std::io::stdout().write(buf);
-        }
-
-        Ok(buf.len())
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        if !TUI_ENABLED.load(Ordering::SeqCst) {
-            std::io::stdout().flush()?;
-        }
-
-        Ok(())
-    }
-}
-
 /// Formats a tracing field and value, removing any internal fields from the final output.
 fn format_field(field_name: &str, value: &dyn Debug) -> String {
     let mut out = String::new();
@@ -398,3 +405,26 @@ impl Display for TraceSrc {
         }
     }
 }
+
+/// Retrieve and print the relative elapsed wall-clock time since an epoch.
+///
+/// The `Default` implementation for `Uptime` makes the epoch the current time.
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub struct PrettyUptime {
+    epoch: Instant,
+}
+
+impl Default for PrettyUptime {
+    fn default() -> Self {
+        Self {
+            epoch: Instant::now(),
+        }
+    }
+}
+
+impl FormatTime for PrettyUptime {
+    fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
+        let e = self.epoch.elapsed();
+        write!(w, "{:4}.{:2}s", e.as_secs(), e.subsec_millis())
+    }
+}

+ 39 - 39
packages/cli/src/main.rs

@@ -4,7 +4,7 @@
 #![cfg_attr(docsrs, feature(doc_cfg))]
 
 mod assets;
-mod builder;
+mod build;
 mod bundle_utils;
 mod cli;
 mod config;
@@ -13,65 +13,65 @@ mod dx_build_info;
 mod error;
 mod fastfs;
 mod filemap;
+mod logging;
 mod metadata;
 mod platform;
 mod profiles;
 mod rustup;
 mod serve;
 mod settings;
+mod slog;
 mod tooling;
-mod tracer;
 
-pub(crate) use builder::*;
+pub(crate) use build::*;
 pub(crate) use cli::*;
 pub(crate) use config::*;
 pub(crate) use dioxus_crate::*;
+pub(crate) use dioxus_dx_wire_format::*;
 pub(crate) use error::*;
 pub(crate) use filemap::*;
+pub(crate) use logging::*;
 pub(crate) use platform::*;
 pub(crate) use rustup::*;
 pub(crate) use settings::*;
-pub(crate) use tracer::*;
-
-use anyhow::Context;
-use clap::Parser;
-use Commands::*;
 
 #[tokio::main]
-async fn main() -> anyhow::Result<()> {
+async fn main() {
     // If we're being ran as a linker (likely from ourselves), we want to act as a linker instead.
     if let Some(link_action) = link::LinkAction::from_env() {
         return link_action.run();
     }
 
-    // Start the tracer so it captures logs from the build engine before we start the builder
-    TraceController::initialize();
-
-    match Cli::parse().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(mut opts) => opts.build_it().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:"),
-
-        Doctor(opts) => opts.run().await.context("🚫 Checking project failed:"),
-    }
+    let args = TraceController::initialize();
+    let result = match args.action {
+        Commands::Translate(opts) => opts.translate(),
+        Commands::New(opts) => opts.create(),
+        Commands::Init(opts) => opts.init(),
+        Commands::Config(opts) => opts.config(),
+        Commands::Autoformat(opts) => opts.autoformat(),
+        Commands::Check(opts) => opts.check().await,
+        Commands::Clean(opts) => opts.clean().await,
+        Commands::Build(opts) => opts.run_cmd().await,
+        Commands::Serve(opts) => opts.serve().await,
+        Commands::Bundle(opts) => opts.bundle().await,
+        Commands::Run(opts) => opts.run().await,
+        Commands::Doctor(opts) => opts.run().await,
+    };
+
+    // Provide a structured output for third party tools that can consume the output of the CLI
+    match result {
+        Ok(output) => {
+            tracing::debug!(json = ?output);
+        }
+        Err(err) => {
+            tracing::error!(
+                ?err,
+                json = ?StructuredOutput::Error {
+                    message: format!("{err:?}"),
+                },
+            );
+
+            std::process::exit(1);
+        }
+    };
 }

+ 1 - 4
packages/cli/src/platform.rs

@@ -174,10 +174,7 @@ impl Platform {
                     Some(Platform::Linux)
                 }
             }
-            "mobile" => {
-                tracing::warn!("Could not autodetect mobile platform. Mobile platforms must be explicitly specified. Pass `--platform ios` or `--platform android` instead.");
-                None
-            }
+            "mobile" => None,
             "liveview" => Some(Platform::Liveview),
             "server" => Some(Platform::Server),
             _ => None,

+ 38 - 8
packages/cli/src/serve/handle.rs

@@ -68,6 +68,7 @@ impl AppHandle {
         // These need to be stable within a release version (ie 0.6.0)
         let mut envs = vec![
             ("DIOXUS_CLI_ENABLED", "true".to_string()),
+            ("RUST_BACKTRACE", "1".to_string()),
             (
                 dioxus_cli_config::DEVSERVER_RAW_ADDR_ENV,
                 devserver_ip.to_string(),
@@ -116,7 +117,7 @@ impl AppHandle {
 
             // https://developer.android.com/studio/run/emulator-commandline
             Platform::Android => {
-                tracing::error!("Android is not yet supported, sorry!");
+                self.open_android_sim(envs).await?;
                 None
             }
 
@@ -159,7 +160,7 @@ impl AppHandle {
         // we won't actually be using the build dir.
         let asset_dir = match self.runtime_asst_dir.as_ref() {
             Some(dir) => dir.to_path_buf().join("assets/"),
-            None => self.app.asset_dir(),
+            None => self.app.build.asset_dir(),
         };
 
         tracing::debug!("Hotreloading asset {changed_file:?} in target {asset_dir:?}");
@@ -237,13 +238,16 @@ impl AppHandle {
     /// TODO(jon): we should probably check if there's a simulator running before trying to install,
     /// and open the simulator if we have to.
     async fn open_ios_sim(&mut self, envs: Vec<(&str, String)>) -> Result<Child> {
-        tracing::debug!("Installing app to simulator {:?}", self.app.app_dir());
+        tracing::debug!(
+            "Installing app to simulator {:?}",
+            self.app.build.root_dir()
+        );
 
         let res = Command::new("xcrun")
             .arg("simctl")
             .arg("install")
             .arg("booted")
-            .arg(self.app.app_dir())
+            .arg(self.app.build.root_dir())
             .stderr(Stdio::piped())
             .stdout(Stdio::piped())
             .output()
@@ -262,15 +266,13 @@ impl AppHandle {
             .arg("launch")
             .arg("--console")
             .arg("booted")
-            .arg("com.dioxuslabs")
+            .arg(self.app.bundle_identifier())
             .envs(ios_envs)
             .stderr(Stdio::piped())
             .stdout(Stdio::piped())
             .kill_on_drop(true)
             .spawn()?;
 
-        tracing::debug!("Launched app on simulator with exit code: {child:?}");
-
         Ok(child)
     }
 
@@ -309,7 +311,7 @@ impl AppHandle {
         // # xcrun devicectl device process resume --device "${DEVICE_UUID}" --pid "${STATUS_PID}" > "${XCRUN_DEVICE_PROCESS_RESUME_LOG_DIR}" 2>&1
 
         use serde_json::Value;
-        let app_path = self.app.app_dir();
+        let app_path = self.app.build.root_dir();
 
         install_app(&app_path).await?;
 
@@ -448,4 +450,32 @@ impl AppHandle {
 
         unimplemented!("dioxus-cli doesn't support ios devices yet.")
     }
+
+    async fn open_android_sim(&self, envs: Vec<(&str, String)>) -> Result<()> {
+        // Install
+        // adb install -r app-debug.apk
+        let _output = Command::new("adb")
+            .arg("install")
+            .arg("-r")
+            .arg(self.app.apk_path())
+            .stderr(Stdio::piped())
+            .stdout(Stdio::piped())
+            .output()
+            .await?;
+
+        // adb shell am start -n com.example.androidfinal/com.example.androidfinal.MainActivity
+        let _output = Command::new("adb")
+            .arg("shell")
+            .arg("am")
+            .arg("start")
+            .arg("-n")
+            .arg("com.example.androidfinal/com.example.androidfinal.MainActivity")
+            .envs(envs)
+            .stderr(Stdio::piped())
+            .stdout(Stdio::piped())
+            .output()
+            .await?;
+
+        Ok(())
+    }
 }

+ 17 - 16
packages/cli/src/serve/mod.rs

@@ -1,7 +1,4 @@
-use crate::{
-    BuildUpdate, Builder, DioxusCrate, Error, Platform, Result, ServeArgs, TraceController,
-    TraceSrc,
-};
+use crate::{BuildUpdate, Builder, Error, Platform, Result, ServeArgs, TraceController, TraceSrc};
 
 mod ansi_buffer;
 mod detect;
@@ -40,9 +37,13 @@ pub(crate) use watcher::*;
 /// - I'd love to be able to configure the CLI while it's running so we can change settings on the fly.
 /// - 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<()> {
+pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
+    // Redirect all logging the cli logger
     let mut tracer = TraceController::redirect();
 
+    // Load the krate and resolve the server args against it - this might log so do it after we turn on the tracer first
+    let krate = args.load_krate()?;
+
     // Note that starting the builder will queue up a build immediately
     let mut builder = Builder::start(&krate, args.build_args())?;
     let mut devserver = WebServer::start(&krate, &args)?;
@@ -53,15 +54,15 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()>
     // This is our default splash screen. We might want to make this a fancier splash screen in the future
     // Also, these commands might not be the most important, but it's all we've got enabled right now
     tracing::info!(
-        r#"Serving your Dioxus app: {} 🚀
-
-               - Press `ctrl+c` to exit the server
-               - Press `r` to rebuild the app
-               - Press `o` to open the app
-               - Press `t` to toggle cargo output
-               - Press `/` for more commands and shortcuts
-
-               Learn more at https://dioxuslabs.com/learn/0.6/getting_started"#,
+        r#"-----------------------------------------------------------------
+                Serving your Dioxus app: {} 🚀
+                Press `ctrl+c` to exit the server
+                Press `r` to rebuild the app
+                Press `o` to open the app
+                • Press `v` to toggle verbose logging
+                Press `/` for more commands and shortcuts
+                Learn more at https://dioxuslabs.com/learn/0.6/getting_started
+               ----------------------------------------------------------------"#,
         krate.executable_name()
     );
 
@@ -146,7 +147,7 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()>
                 screen.new_build_update(&update);
 
                 // And then update the websocketed clients with the new build status in case they want it
-                devserver.new_build_update(&update).await;
+                devserver.new_build_update(&update, &builder).await;
 
                 // And then open the app if it's ready
                 // todo: there might be more things to do here that require coordination with other pieces of the CLI
@@ -213,6 +214,7 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()>
                 // `Full rebuild:` to line up with
                 // `Hotreloading:` to keep the alignment during long edit sessions
                 tracing::info!("Full rebuild: triggered manually");
+
                 builder.rebuild(args.build_arguments.clone());
                 runner.file_map.force_rebuild();
                 devserver.start_build().await
@@ -248,7 +250,6 @@ pub(crate) async fn serve_all(args: ServeArgs, krate: DioxusCrate) -> Result<()>
     _ = devserver.shutdown().await;
     _ = screen.shutdown();
     builder.abort_all();
-    tracer.shutdown();
 
     if let Err(err) = err {
         eprintln!("Exiting with error: {}", err);

+ 95 - 112
packages/cli/src/serve/output.rs

@@ -99,8 +99,8 @@ impl Output {
             more_modal_open: false,
             pending_logs: VecDeque::new(),
             throbber: RefCell::new(throbber_widgets_tui::ThrobberState::default()),
-            trace: cfg.trace,
-            verbose: cfg.verbose,
+            trace: crate::logging::VERBOSITY.get().unwrap().trace,
+            verbose: crate::logging::VERBOSITY.get().unwrap().verbose,
             tick_animation: false,
             tick_interval: {
                 let mut interval = tokio::time::interval(Duration::from_millis(TICK_RATE_MS));
@@ -152,10 +152,8 @@ impl Output {
                 .execute(DisableBracketedPaste)?;
             disable_raw_mode()?;
 
-            // print a few lines to not cut off the output
-            for _ in 0..3 {
-                println!();
-            }
+            // print a line to force the cursor down (no tearing)
+            println!();
         }
 
         Ok(())
@@ -331,10 +329,12 @@ impl Output {
     /// we won't be drawing it.
     pub(crate) fn new_build_update(&mut self, update: &BuildUpdate) {
         match update {
-            BuildUpdate::CompilerMessage { .. } => {}
-            BuildUpdate::Progress { .. } => self.tick_animation = true,
+            BuildUpdate::Progress {
+                stage: BuildStage::Starting { .. },
+            } => self.tick_animation = true,
             BuildUpdate::BuildReady { .. } => self.tick_animation = false,
             BuildUpdate::BuildFailed { .. } => self.tick_animation = false,
+            _ => {}
         }
     }
 
@@ -381,7 +381,7 @@ impl Output {
         let mut area = frame.area();
         area.width = area.width.clamp(0, VIEWPORT_MAX_WIDTH);
 
-        let [_top, body, bottom] = Layout::vertical([
+        let [_top, body, _bottom] = Layout::vertical([
             Constraint::Length(1),
             Constraint::Fill(1),
             Constraint::Length(1),
@@ -391,7 +391,6 @@ impl Output {
 
         self.render_borders(frame, area);
         self.render_body(frame, body, state);
-        self.render_bottom_row(frame, bottom, state);
         self.render_body_title(frame, _top, state);
     }
 
@@ -512,6 +511,7 @@ impl Output {
             BuildStage::Failed => lines.push("Failed".red()),
             BuildStage::Aborted => lines.push("Aborted".red()),
             BuildStage::Restarting => lines.push("Restarting".yellow()),
+            _ => {}
         };
 
         frame.render_widget(Line::from(lines), status_line);
@@ -712,23 +712,6 @@ impl Output {
         );
     }
 
-    /// Render the version number on the bottom right
-    fn render_bottom_row(&self, frame: &mut Frame, area: Rect, _state: RenderState) {
-        // Split the area into two chunks
-        let row = Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)]).split(area);
-
-        frame.render_widget(
-            Paragraph::new(Line::from(vec![
-                // "🧬 dx".dark_gray(),
-                // " ".dark_gray(),
-                // self.dx_version.as_str().dark_gray(),
-                // " ".dark_gray(),
-            ]))
-            .right_aligned(),
-            row[1],
-        );
-    }
-
     /// Render borders around the terminal, forcing an inner clear while we're at it
     fn render_borders(&self, frame: &mut Frame, area: Rect) {
         frame.render_widget(ratatui::widgets::Clear, area);
@@ -784,7 +767,7 @@ impl Output {
 
         // Render the log into an ansi string
         // We're going to add some metadata to it like the timestamp and source and then dump it to the raw ansi sequences we need to send to crossterm
-        let output_sequence = tracemsg_to_ansi_string(log, term_size.width);
+        let output_sequence = Self::tracemsg_to_ansi_string(log, term_size.width);
 
         // Get the lines of the output sequence and their overflow
         let lines = output_sequence.lines().collect::<Vec<_>>();
@@ -863,94 +846,94 @@ impl Output {
             false => VIEWPORT_HEIGHT_SMALL,
         }
     }
-}
 
-fn tracemsg_to_ansi_string(log: TraceMsg, term_width: u16) -> String {
-    let rendered = match log.content {
-        TraceContent::Cargo(msg) => msg.message.rendered.unwrap_or_default(),
-        TraceContent::Text(text) => text,
-    };
-
-    // Create a paragraph widget using the log line itself
-    // From here on out, we want to work with the escaped ansi string and the "real lines" to be printed
-    //
-    // We make a special case for lines that look like frames (ie ==== or ---- or ------) and make them
-    // dark gray, just for readability.
-    //
-    // todo(jon): refactor this out to accept any widget, not just paragraphs
-    let paragraph = Paragraph::new({
-        use ansi_to_tui::IntoText;
-        use chrono::Timelike;
-
-        let mut text = Text::default();
-
-        for (idx, raw_line) in rendered.lines().enumerate() {
-            let line_as_text = raw_line.into_text().unwrap();
-            let is_pretending_to_be_frame = raw_line
-                .chars()
-                .all(|c| c == '=' || c == '-' || c == ' ' || c == '─');
-
-            for (subline_idx, line) in line_as_text.lines.into_iter().enumerate() {
-                let mut out_line = if idx == 0 && subline_idx == 0 {
-                    let mut formatted_line = Line::default();
-
-                    formatted_line.push_span(
-                        Span::raw(format!(
-                            "{:02}:{:02}:{:02} ",
-                            log.timestamp.hour(),
-                            log.timestamp.minute(),
-                            log.timestamp.second()
-                        ))
-                        .dark_gray(),
-                    );
-                    formatted_line.push_span(
-                        Span::raw(format!(
-                            "[{src}] {padding}",
-                            src = log.source,
-                            padding =
-                                " ".repeat(3usize.saturating_sub(log.source.to_string().len()))
-                        ))
-                        .style(match log.source {
-                            TraceSrc::App(_platform) => Style::new().blue(),
-                            TraceSrc::Dev => Style::new().magenta(),
-                            TraceSrc::Build => Style::new().yellow(),
-                            TraceSrc::Bundle => Style::new().magenta(),
-                            TraceSrc::Cargo => Style::new().yellow(),
-                            TraceSrc::Unknown => Style::new().gray(),
-                        }),
-                    );
-
-                    for span in line.spans {
-                        formatted_line.push_span(span);
-                    }
+    fn tracemsg_to_ansi_string(log: TraceMsg, term_width: u16) -> String {
+        let rendered = match log.content {
+            TraceContent::Cargo(msg) => msg.message.rendered.unwrap_or_default(),
+            TraceContent::Text(text) => text,
+        };
 
-                    formatted_line
-                } else {
-                    line
-                };
+        // Create a paragraph widget using the log line itself
+        // From here on out, we want to work with the escaped ansi string and the "real lines" to be printed
+        //
+        // We make a special case for lines that look like frames (ie ==== or ---- or ------) and make them
+        // dark gray, just for readability.
+        //
+        // todo(jon): refactor this out to accept any widget, not just paragraphs
+        let paragraph = Paragraph::new({
+            use ansi_to_tui::IntoText;
+            use chrono::Timelike;
+
+            let mut text = Text::default();
+
+            for (idx, raw_line) in rendered.lines().enumerate() {
+                let line_as_text = raw_line.into_text().unwrap();
+                let is_pretending_to_be_frame = raw_line
+                    .chars()
+                    .all(|c| c == '=' || c == '-' || c == ' ' || c == '─');
+
+                for (subline_idx, line) in line_as_text.lines.into_iter().enumerate() {
+                    let mut out_line = if idx == 0 && subline_idx == 0 {
+                        let mut formatted_line = Line::default();
+
+                        formatted_line.push_span(
+                            Span::raw(format!(
+                                "{:02}:{:02}:{:02} ",
+                                log.timestamp.hour(),
+                                log.timestamp.minute(),
+                                log.timestamp.second()
+                            ))
+                            .dark_gray(),
+                        );
+                        formatted_line.push_span(
+                            Span::raw(format!(
+                                "[{src}] {padding}",
+                                src = log.source,
+                                padding =
+                                    " ".repeat(3usize.saturating_sub(log.source.to_string().len()))
+                            ))
+                            .style(match log.source {
+                                TraceSrc::App(_platform) => Style::new().blue(),
+                                TraceSrc::Dev => Style::new().magenta(),
+                                TraceSrc::Build => Style::new().yellow(),
+                                TraceSrc::Bundle => Style::new().magenta(),
+                                TraceSrc::Cargo => Style::new().yellow(),
+                                TraceSrc::Unknown => Style::new().gray(),
+                            }),
+                        );
+
+                        for span in line.spans {
+                            formatted_line.push_span(span);
+                        }
+
+                        formatted_line
+                    } else {
+                        line
+                    };
+
+                    if is_pretending_to_be_frame {
+                        out_line = out_line.dark_gray();
+                    }
 
-                if is_pretending_to_be_frame {
-                    out_line = out_line.dark_gray();
+                    text.lines.push(out_line);
                 }
-
-                text.lines.push(out_line);
             }
-        }
 
-        text
-    });
-
-    // We want to get the escaped ansii string and then by dumping the paragraph as ascii codes (again)
-    //
-    // This is important because the line_count method on paragraph takes into account the width of these codes
-    // the 3000 clip width is to bound log lines to a reasonable memory usage
-    // We could consider reusing this buffer since it's a lot to allocate, but log printing is not the
-    // slowest thing in the world and allocating is pretty fast...
-    //
-    // After we've dumped the ascii out, we want to call "trim_end" which ensures we don't attempt
-    // to print extra characters as lines, since AnsiStringBuffer will in fact attempt to print empty
-    // cells as characters. That might not actually be important, but we want to shrink the buffer
-    // before printing it
-    let line_count = paragraph.line_count(term_width);
-    AnsiStringBuffer::new(3000, line_count as u16).render(&paragraph)
+            text
+        });
+
+        // We want to get the escaped ansii string and then by dumping the paragraph as ascii codes (again)
+        //
+        // This is important because the line_count method on paragraph takes into account the width of these codes
+        // the 3000 clip width is to bound log lines to a reasonable memory usage
+        // We could consider reusing this buffer since it's a lot to allocate, but log printing is not the
+        // slowest thing in the world and allocating is pretty fast...
+        //
+        // After we've dumped the ascii out, we want to call "trim_end" which ensures we don't attempt
+        // to print extra characters as lines, since AnsiStringBuffer will in fact attempt to print empty
+        // cells as characters. That might not actually be important, but we want to shrink the buffer
+        // before printing it
+        let line_count = paragraph.line_count(term_width);
+        AnsiStringBuffer::new(3000, line_count as u16).render(&paragraph)
+    }
 }

+ 14 - 3
packages/cli/src/serve/runner.rs

@@ -111,6 +111,16 @@ impl AppRunner {
         // todo(jon): we should allow rebinding to the same port in fullstack itself
         tokio::time::sleep(std::time::Duration::from_millis(100)).await;
 
+        // Add some cute logging
+        if self.builds_opened == 0 {
+            tracing::info!(
+                "Build completed successfully in {:?}ms, launching app! 💫",
+                app.app.time_taken.as_millis()
+            );
+        } else {
+            tracing::info!("Build completed in {:?}ms", app.app.time_taken.as_millis());
+        }
+
         // Start the new app before we kill the old one to give it a little bit of time
         let mut handle = AppHandle::new(app).await?;
         handle
@@ -120,6 +130,7 @@ impl AppRunner {
                 self.builds_opened == 0 && should_open_web,
             )
             .await?;
+
         self.builds_opened += 1;
         self.running.insert(platform, handle);
 
@@ -264,17 +275,17 @@ impl AppRunner {
                     .arg("simctl")
                     .arg("get_app_container")
                     .arg("booted")
-                    .arg("com.dioxuslabs")
+                    .arg(runner.app.bundle_identifier())
                     .output()
                     .await;
 
                 if let Ok(res) = res {
-                    tracing::debug!("Using runtime asset dir: {:?}", res);
+                    tracing::trace!("Using runtime asset dir: {:?}", res);
 
                     if let Ok(out) = String::from_utf8(res.stdout) {
                         let out = out.trim();
 
-                        tracing::debug!("Setting Runtime asset dir: {out:?}");
+                        tracing::trace!("Setting Runtime asset dir: {out:?}");
                         runner.runtime_asst_dir = Some(PathBuf::from(out));
                     }
                 }

+ 15 - 7
packages/cli/src/serve/server.rs

@@ -204,7 +204,11 @@ impl WebServer {
     }
 
     /// Sends an updated build status to all clients.
-    pub(crate) async fn new_build_update(&mut self, update: &BuildUpdate) {
+    pub(crate) async fn new_build_update(
+        &mut self,
+        update: &BuildUpdate,
+        builder: &super::Builder,
+    ) {
         match update {
             BuildUpdate::Progress { stage } => {
                 // Todo(miles): wire up more messages into the splash screen UI
@@ -220,11 +224,13 @@ impl WebServer {
                         krate,
                         ..
                     } => {
-                        self.build_status.set(Status::Building {
-                            progress: (*current as f64 / *total as f64).clamp(0.0, 1.0),
-                            build_message: format!("{krate} compiling"),
-                        });
-                        self.send_build_status().await;
+                        if !builder.is_finished() {
+                            self.build_status.set(Status::Building {
+                                progress: (*current as f64 / *total as f64).clamp(0.0, 1.0),
+                                build_message: format!("{krate} compiling"),
+                            });
+                            self.send_build_status().await;
+                        }
                     }
                     BuildStage::OptimizingWasm {} => {}
                     BuildStage::Aborted => {}
@@ -250,7 +256,7 @@ impl WebServer {
             return;
         }
 
-        tracing::debug!("Sending hotreload to clients {:?}", reload);
+        tracing::trace!("Sending hotreload to clients {:?}", reload);
 
         let msg = DevserverMsg::HotReload(reload);
         let msg = serde_json::to_string(&msg).unwrap();
@@ -281,6 +287,8 @@ impl WebServer {
 
     /// Tells all clients to reload if possible for new changes.
     pub(crate) async fn send_reload_command(&mut self) {
+        tracing::trace!("Sending reload to toast");
+
         tokio::time::sleep(std::time::Duration::from_millis(500)).await;
 
         self.build_status.set(Status::Ready);

+ 1 - 0
packages/cli/src/slog.rs

@@ -0,0 +1 @@
+

+ 0 - 1
packages/config-macro/Cargo.toml

@@ -19,7 +19,6 @@ quote = { workspace = true }
 [features]
 default = []
 fullstack = []
-static-generation = []
 desktop = []
 mobile = []
 web = []

+ 0 - 1
packages/config-macro/src/lib.rs

@@ -33,6 +33,5 @@ define_config_macro!(web if feature = "web");
 define_config_macro!(desktop if feature = "desktop");
 define_config_macro!(mobile if feature = "mobile");
 define_config_macro!(fullstack if feature = "fullstack");
-define_config_macro!(static_generation if feature = "static-generation");
 define_config_macro!(ssr if feature = "ssr");
 define_config_macro!(liveview if feature = "liveview");

+ 3 - 0
packages/desktop/.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    // "rust-analyzer.cargo.target": "aarch64-linux-android"
+}

+ 6 - 1
packages/desktop/Cargo.toml

@@ -83,6 +83,10 @@ objc_id = "0.1.1"
 # use rustls on android
 [target.'cfg(target_os = "android")'.dependencies]
 tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"]}
+jni = "0.21.1"
+ndk = { version = "0.9.0" }
+ndk-sys = { version = "0.6.0" }
+ndk-context = { version = "0.1.1" }
 
 # use native tls on other platforms
 [target.'cfg(not(target_os = "android"))'.dependencies]
@@ -97,11 +101,12 @@ objc = "0.2.7"
 lazy-js-bundle = { workspace = true }
 
 [features]
-default = ["tokio_runtime", "wry/objc-exception", "devtools"]
+default = ["tokio_runtime", "exception", "devtools"]
 tokio_runtime = ["dep:tokio"]
 fullscreen = ["wry/fullscreen"]
 transparent = ["wry/transparent"]
 devtools = ["wry/devtools", "dep:dioxus-devtools", "dioxus-signals"]
+exception = ["wry/objc-exception"]
 gnu = []
 
 [package.metadata.docs.rs]

+ 4 - 2
packages/desktop/src/launch.rs

@@ -75,7 +75,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf
                         webview.dom.in_runtime(|| {
                             ScopeId::ROOT.in_runtime(|| {
                                 let e = eval(
-                                    r#" 
+                                    r#"
                                     const xPos = await dioxus.recv();
                                     const yPos = await dioxus.recv();
                                     window.interpreter.handleWindowsDragOver(xPos, yPos)
@@ -121,7 +121,9 @@ pub fn launch_virtual_dom(virtual_dom: VirtualDom, desktop_config: Config) -> !
     }
 
     #[cfg(not(feature = "tokio_runtime"))]
-    launch_virtual_dom_blocking(virtual_dom, desktop_config);
+    {
+        launch_virtual_dom_blocking(virtual_dom, desktop_config);
+    }
 }
 
 /// Launches the WebView and runs the event loop, with configuration and root props.

+ 50 - 0
packages/desktop/src/protocol.rs

@@ -84,6 +84,17 @@ fn serve_asset(request: Request<Vec<u8>>) -> Result<Response<Vec<u8>>> {
             .as_ref(),
     );
 
+    // Attempt to serve from the asset dir on android using its loader
+    #[cfg(target_os = "android")]
+    {
+        if let Some(asset) = to_java_load_asset(request.uri().path()) {
+            return Ok(Response::builder()
+                .header("Content-Type", get_mime_by_ext(&uri_path))
+                .header("Access-Control-Allow-Origin", "*")
+                .body(asset)?);
+        }
+    }
+
     // If the asset doesn't exist, or starts with `/assets/`, then we'll try to serve out of the bundle
     // This lets us handle both absolute and relative paths without being too "special"
     // It just means that our macos bundle is a little "special" because we need to place an `assets`
@@ -261,3 +272,42 @@ fn get_mime_by_ext(trimmed: &Path) -> &'static str {
         None => "application/octet-stream",
     }
 }
+
+#[cfg(target_os = "android")]
+pub(crate) fn to_java_load_asset(filepath: &str) -> Option<Vec<u8>> {
+    use std::{io::Read, ptr::NonNull};
+
+    let ctx = ndk_context::android_context();
+    let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }.unwrap();
+    let mut env = vm.attach_current_thread().unwrap();
+
+    // Query the Asset Manager
+    let asset_manager_ptr = env
+        .call_method(
+            unsafe { jni::objects::JObject::from_raw(ctx.context().cast()) },
+            "getAssets",
+            "()Landroid/content/res/AssetManager;",
+            &[],
+        )
+        .expect("Failed to get asset manager")
+        .l()
+        .expect("Failed to get asset manager as object");
+
+    unsafe {
+        let asset_manager =
+            ndk_sys::AAssetManager_fromJava(env.get_native_interface(), *asset_manager_ptr);
+
+        let asset_manager = ndk::asset::AssetManager::from_ptr(
+            NonNull::new(asset_manager).expect("Invalid asset manager"),
+        );
+
+        let normalized = filepath
+            .trim_start_matches("/assets/")
+            .trim_start_matches('/');
+
+        let cstr = std::ffi::CString::new(normalized).unwrap();
+
+        let mut asset = asset_manager.open(&cstr)?;
+        Some(asset.buffer().unwrap().to_vec())
+    }
+}

+ 4 - 17
packages/devtools/src/lib.rs

@@ -37,26 +37,13 @@ pub fn connect(endpoint: String, mut callback: impl FnMut(DevserverMsg) + Send +
     std::thread::spawn(move || {
         let (mut websocket, _req) = match tungstenite::connect(endpoint.clone()) {
             Ok((websocket, req)) => (websocket, req),
-            Err(err) => {
-                eprintln!(
-                    "Failed to connect to devserver at {} because {}",
-                    endpoint, err
-                );
-                return;
-            }
+            Err(_) => return,
         };
 
         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);
+            if let tungstenite::Message::Text(text) = msg {
+                if let Ok(msg) = serde_json::from_str(&text) {
+                    callback(msg);
                 }
             }
         }

+ 2 - 7
packages/dioxus/Cargo.toml

@@ -24,13 +24,11 @@ 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-static-site-generation = { workspace = true, optional = true }
 dioxus-liveview = { workspace = true, optional = true }
 dioxus-ssr = { workspace = true, optional = true }
 manganis = { 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-devtools = { workspace = true, optional = true }
@@ -55,12 +53,10 @@ router = ["dep:dioxus-router"]
 fullstack = ["dep:dioxus-fullstack", "dioxus-config-macro/fullstack", "dep:serde"]
 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"]
+web = ["dep:dioxus-web", "dioxus-fullstack?/web", "dioxus-config-macro/web"]
 ssr = ["dep:dioxus-ssr", "dioxus-config-macro/ssr"]
 liveview = ["dep:dioxus-liveview", "dioxus-config-macro/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", "ssr", "dioxus-liveview?/axum"]
 
 # This feature just disables the no-renderer-enabled warning
 third-party-renderer = []
@@ -90,6 +86,5 @@ features = [
     "hooks",
     "html",
     "liveview",
-    "static-generation",
     "server"
 ]

+ 14 - 60
packages/dioxus/src/launch.rs

@@ -16,6 +16,7 @@ pub struct LaunchBuilder {
 }
 
 pub type LaunchFn = fn(fn() -> Element, Vec<ContextFn>, Vec<Box<dyn Any>>);
+
 /// A context function is a Send and Sync closure that returns a boxed trait object
 pub type ContextFn = Box<dyn Fn() -> Box<dyn Any> + Send + Sync + 'static>;
 
@@ -32,10 +33,9 @@ impl LaunchBuilder {
             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```"
+            note = "No renderer is enabled. You must enable a renderer feature on the dioxus crate before calling the launch function.\nAdd `web`, `desktop`, `mobile`, or `fullstack` 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 {
@@ -82,22 +82,6 @@ impl LaunchBuilder {
         }
     }
 
-    /// Launch your static site generation application.
-    #[cfg(all(feature = "static-generation", feature = "server"))]
-    #[cfg_attr(
-        docsrs,
-        doc(cfg(all(feature = "static-generation", feature = "server")))
-    )]
-    pub fn static_generation() -> LaunchBuilder {
-        LaunchBuilder {
-            launch_fn: |root, contexts, cfg| {
-                dioxus_static_site_generation::launch::launch(root, contexts, cfg)
-            },
-            contexts: Vec::new(),
-            configs: Vec::new(),
-        }
-    }
-
     /// Launch your fullstack application.
     #[cfg(feature = "mobile")]
     #[cfg_attr(docsrs, doc(cfg(feature = "mobile")))]
@@ -188,19 +172,10 @@ impl LaunchBuilder {
         (self.launch_fn)(app, self.contexts, cfg);
     }
 
-    // Static generation is the only platform that may exit. We can't use the `!` type here
-    #[cfg(any(feature = "static-generation", feature = "web"))]
     /// Launch your application.
     pub fn launch(self, app: fn() -> Element) {
         self.launch_inner(app);
     }
-
-    #[cfg(not(any(feature = "static-generation", feature = "web")))]
-    /// Launch your application.
-    pub fn launch(self, app: fn() -> Element) -> ! {
-        self.launch_inner(app);
-        unreachable!("Launching an application will never exit")
-    }
 }
 
 /// Re-export the platform we expect the user wants
@@ -209,7 +184,6 @@ impl LaunchBuilder {
 /// - `fullstack`
 /// - `desktop`
 /// - `mobile`
-/// - `static-generation`
 /// - `web`
 /// - `liveview`
 mod current_platform {
@@ -227,20 +201,12 @@ mod current_platform {
         not(feature = "desktop"),
         not(all(feature = "fullstack", feature = "server"))
     ))]
-    pub use dioxus_mobile::launch::*;
-
-    #[cfg(all(
-        all(feature = "static-generation", feature = "server"),
-        not(all(feature = "fullstack", feature = "server")),
-        not(feature = "desktop"),
-        not(feature = "mobile")
-    ))]
-    pub use dioxus_static_site_generation::launch::*;
+    pub use dioxus_mobile::launch_bindings::*;
 
     #[cfg(all(
         feature = "web",
         not(all(feature = "fullstack", feature = "server")),
-        not(all(feature = "static-generation", feature = "server")),
+        not(all(feature = "server")),
         not(feature = "desktop"),
         not(feature = "mobile"),
     ))]
@@ -255,7 +221,7 @@ mod current_platform {
     #[cfg(all(
         feature = "liveview",
         not(all(feature = "fullstack", feature = "server")),
-        not(all(feature = "static-generation", feature = "server")),
+        not(all(feature = "server")),
         not(feature = "desktop"),
         not(feature = "mobile"),
         not(feature = "web"),
@@ -265,7 +231,7 @@ mod current_platform {
     #[cfg(not(any(
         feature = "liveview",
         all(feature = "fullstack", feature = "server"),
-        all(feature = "static-generation", feature = "server"),
+        all(feature = "server"),
         feature = "desktop",
         feature = "mobile",
         feature = "web",
@@ -274,7 +240,7 @@ mod current_platform {
         root: fn() -> dioxus_core::Element,
         contexts: Vec<super::ContextFn>,
         platform_config: Vec<Box<dyn std::any::Any>>,
-    ) -> ! {
+    ) {
         #[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.");
 
@@ -282,23 +248,11 @@ mod current_platform {
     }
 }
 
-// ! 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.
-        pub fn launch(app: fn() -> Element) -> $($return_type)* {
-            #[allow(deprecated)]
-            LaunchBuilder::new().launch(app)
-        }
-    };
+pub fn launch(app: fn() -> Element) {
+    #[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")]
 fn web_launch(
     root: fn() -> dioxus_core::Element,
@@ -306,7 +260,7 @@ fn web_launch(
     platform_config: Vec<Box<dyn std::any::Any>>,
 ) {
     // If the server feature is enabled, launch the client with hydration enabled
-    #[cfg(any(feature = "static-generation", feature = "fullstack"))]
+    #[cfg(feature = "fullstack")]
     {
         let platform_config = platform_config
             .into_iter()
@@ -319,12 +273,11 @@ fn web_launch(
             for context in contexts {
                 vdom.insert_any_root_context(context());
             }
+
             #[cfg(feature = "document")]
             {
                 #[cfg(feature = "fullstack")]
                 use dioxus_fullstack::document;
-                #[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::Document>;
                 vdom.provide_root_context(document);
@@ -334,6 +287,7 @@ fn web_launch(
 
         dioxus_web::launch::launch_virtual_dom(factory(), platform_config)
     }
-    #[cfg(not(any(feature = "static-generation", feature = "fullstack")))]
+
+    #[cfg(not(any(feature = "fullstack")))]
     dioxus_web::launch::launch(root, contexts, platform_config);
 }

+ 1 - 17
packages/dioxus/src/lib.rs

@@ -19,8 +19,7 @@
 //! - `mobile`: enables the mobile platform
 //! - `web`: enables the web platform. If the fullstack platform is enabled, this will set the fullstack platform to client mode
 //! - `liveview`: enables the liveview platform
-//! - `static-generation`: enables the static generation platform. This must be used in combination with the `web` feature for wasm builds and `axum` feature for server builds
-//! - `axum`: enables the axum server with static generation or fullstack and sets the platform to server mode
+//! - `server`: enables the server variant of dioxus
 #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
 #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
 #![cfg_attr(docsrs, feature(doc_cfg))]
@@ -119,13 +118,6 @@ pub mod prelude {
     #[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;
@@ -134,10 +126,6 @@ pub mod prelude {
     #[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, asset, Asset, ImageAsset, ImageType};
@@ -155,10 +143,6 @@ pub use dioxus_router as router;
 #[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;

Some files were not shown because too many files changed in this diff