ソースを参照

Merge branch 'master' into pr/Dangerised/1902

Evan Almloff 1 年間 前
コミット
65d4f922b4
100 ファイル変更2259 行追加3862 行削除
  1. 12 0
      .github/free_space.sh
  2. 65 50
      .github/workflows/main.yml
  3. 4 4
      .gitignore
  4. 473 946
      Cargo.lock
  5. 65 38
      Cargo.toml
  6. 7 4
      README.md
  7. 8 7
      examples/PWA-example/src/main.rs
  8. 0 413
      examples/all_css.rs
  9. 36 55
      examples/all_events.rs
  10. 25 14
      examples/assets/calculator.css
  11. 55 0
      examples/backgrounded_futures.rs
  12. 0 73
      examples/borrowed.rs
  13. 56 57
      examples/calculator.rs
  14. 22 56
      examples/calculator_mutable.rs
  15. 0 22
      examples/callback.rs
  16. 10 8
      examples/clock.rs
  17. 32 38
      examples/compose.rs
  18. 22 17
      examples/control_focus.rs
  19. 36 19
      examples/counter.rs
  20. 77 93
      examples/crm.rs
  21. 13 8
      examples/custom_assets.rs
  22. 18 18
      examples/custom_html.rs
  23. 8 12
      examples/disabled.rs
  24. 46 55
      examples/dog_app.rs
  25. 0 36
      examples/drops.rs
  26. 7 9
      examples/dynamic_asset.rs
  27. 8 11
      examples/error_handle.rs
  28. 7 11
      examples/eval.rs
  29. 0 57
      examples/fermi.rs
  30. 14 10
      examples/file_explorer.rs
  31. 31 39
      examples/file_upload.rs
  32. 11 13
      examples/filedragdrop.rs
  33. 37 43
      examples/flat_router.rs
  34. 4 4
      examples/form.rs
  35. 0 152
      examples/framework_benchmark.rs
  36. 8 8
      examples/generic_component.rs
  37. 20 0
      examples/global.rs
  38. 0 28
      examples/heavy_compute.rs
  39. 3 3
      examples/hello_world.rs
  40. 14 13
      examples/hydration.rs
  41. 0 43
      examples/inlineprops.rs
  42. 33 39
      examples/inputs.rs
  43. 19 20
      examples/link.rs
  44. 5 5
      examples/login_form.rs
  45. 44 0
      examples/memo_chain.rs
  46. 1 1
      examples/mobile_demo/Cargo.toml
  47. 12 8
      examples/mobile_demo/src/lib.rs
  48. 14 17
      examples/multiwindow.rs
  49. 4 4
      examples/nested_listeners.rs
  50. 4 6
      examples/openid_connect_demo/src/main.rs
  51. 0 1
      examples/openid_connect_demo/src/router.rs
  52. 15 17
      examples/openid_connect_demo/src/views/header.rs
  53. 2 2
      examples/openid_connect_demo/src/views/home.rs
  54. 5 5
      examples/openid_connect_demo/src/views/login.rs
  55. 2 2
      examples/openid_connect_demo/src/views/not_found.rs
  56. 15 15
      examples/optional_props.rs
  57. 8 8
      examples/overlay.rs
  58. 14 17
      examples/query_segments.rs
  59. 0 13
      examples/query_segments_demo/Cargo.toml
  60. 25 31
      examples/read_size.rs
  61. 5 9
      examples/readme.rs
  62. 8 18
      examples/reducer.rs
  63. 32 40
      examples/router.rs
  64. 0 58
      examples/rsx_compile_fail.rs
  65. 257 263
      examples/rsx_usage.rs
  66. 9 13
      examples/scroll_to_top.rs
  67. 0 73
      examples/shared_state.rs
  68. 6 9
      examples/shortcut.rs
  69. 7 16
      examples/shorthand.rs
  70. 48 15
      examples/signals.rs
  71. 0 82
      examples/simple_desktop.rs
  72. 5 5
      examples/simple_list.rs
  73. 27 11
      examples/simple_router.rs
  74. 16 16
      examples/spread.rs
  75. 5 6
      examples/ssr.rs
  76. 36 0
      examples/stale_memo.rs
  77. 6 7
      examples/streams.rs
  78. 26 33
      examples/suspense.rs
  79. 35 88
      examples/svg.rs
  80. 0 81
      examples/svg_basic.rs
  81. 2 3
      examples/tailwind/Cargo.toml
  82. 0 1
      examples/tailwind/dist/tailwind3531548035813279582.css
  83. 27 33
      examples/tailwind/src/main.rs
  84. 10 16
      examples/tasks.rs
  85. 5 7
      examples/textarea.rs
  86. 111 144
      examples/todomvc.rs
  87. 7 7
      examples/video_stream.rs
  88. 4 4
      examples/web_component.rs
  89. 35 40
      examples/window_event.rs
  90. 23 33
      examples/window_focus.rs
  91. 7 7
      examples/window_zoom.rs
  92. 5 7
      examples/xss_safety.rs
  93. 1 1
      packages/autofmt/tests/samples/immediate_expr.rsx
  94. 4 4
      packages/autofmt/tests/samples/long.rsx
  95. 3 3
      packages/autofmt/tests/samples/messy_indent.rsx
  96. 2 2
      packages/autofmt/tests/samples/reallylong.rsx
  97. 2 2
      packages/autofmt/tests/samples/trailing_expr.rsx
  98. 2 2
      packages/autofmt/tests/wrong/multi-4sp.rsx
  99. 3 3
      packages/autofmt/tests/wrong/multi-4sp.wrong.rsx
  100. 2 2
      packages/autofmt/tests/wrong/multi-tab.rsx

+ 12 - 0
.github/free_space.sh

@@ -0,0 +1,12 @@
+df -h
+sudo rm -rf ${GITHUB_WORKSPACE}/.git
+sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+sudo rm -rf /usr/share/dotnet
+sudo apt-get remove -y '^ghc-8.*'
+sudo apt-get remove -y '^dotnet-.*'
+sudo apt-get remove -y '^llvm-.*'
+sudo apt-get remove -y 'php.*'
+sudo apt-get remove -y azure-cli google-cloud-sdk hhvm google-chrome-stable firefox powershell mono-devel
+sudo apt-get autoremove -y
+sudo apt-get clean
+df -h

+ 65 - 50
.github/workflows/main.yml

@@ -13,7 +13,6 @@ on:
       - lib.rs
       - Cargo.toml
       - Makefile.toml
-      - playwright-tests/**
 
   pull_request:
     types: [opened, synchronize, reopened, ready_for_review]
@@ -70,12 +69,13 @@ jobs:
       - uses: davidB/rust-cargo-make@v1
       - uses: browser-actions/setup-firefox@latest
       - uses: jetli/wasm-pack-action@v0.4.0
-      - run: sudo rm -rf /usr/share/dotnet
-      - run: sudo rm -rf "$AGENT_TOOLSDIRECTORY"
-      - run: |
-          df -h
-          sudo rm -rf ${GITHUB_WORKSPACE}/.git
-          df -h
+      - name: Free Disk Space (Ubuntu)
+        uses: jlumbroso/free-disk-space@v1.3.1
+        with: # speed things up a bit
+          large-packages: false
+          docker-images: false
+          swap-storage: false
+
       - run: cargo make tests
 
   fmt:
@@ -112,42 +112,43 @@ jobs:
           save-if: ${{ github.ref == 'refs/heads/master' }}
       - run: cargo clippy --workspace --examples --tests -- -D warnings
 
-  miri:
-    if: github.event.pull_request.draft == false
-    name: Miri
-    runs-on: ubuntu-latest
-    env:
-      CARGO_UNSTABLE_SPARSE_REGISTRY: 'true'
-      RUSTFLAGS: -Dwarnings
-      RUST_BACKTRACE: 1
-      MIRIFLAGS: -Zmiri-tag-gc=1
-      # Change to specific Rust release to pin
-      rust_stable: stable
-      rust_nightly: nightly-2023-11-16
-      rust_clippy: 1.70.0
+  # We removed most unsafe that we can, and using nightly doubles our cache size
+  # miri:
+  #   if: github.event.pull_request.draft == false
+  #   name: Miri
+  #   runs-on: ubuntu-latest
+  #   env:
+  #     CARGO_UNSTABLE_SPARSE_REGISTRY: 'true'
+  #     RUSTFLAGS: -Dwarnings
+  #     RUST_BACKTRACE: 1
+  #     MIRIFLAGS: -Zmiri-tag-gc=1
+  #     # Change to specific Rust release to pin
+  #     rust_stable: stable
+  #     rust_nightly: nightly-2023-11-16
+  #     rust_clippy: 1.70.0
 
-    steps:
-      - uses: actions/checkout@v4
-      - uses: ilammy/setup-nasm@v1
-      - name: Install Rust ${{ env.rust_nightly }}
-        uses: dtolnay/rust-toolchain@master
-        with:
-          toolchain: ${{ env.rust_nightly }}
-          components: miri
-      - uses: Swatinem/rust-cache@v2
-        with:
-          cache-all-crates: "true"
-          save-if: ${{ github.ref == 'refs/heads/master' }}
-      - name: miri
-        # Many of tests in tokio/tests and doctests use #[tokio::test] or
-        # #[tokio::main] that calls epoll_create1 that Miri does not support.
-        # run: cargo miri test --features full --lib --no-fail-fast
-        run: |
-          cargo miri test --package dioxus-core -- --exact --nocapture
-          cargo miri test --package dioxus-native-core --test miri_native  -- --exact --nocapture
-        env:
-          MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
-          PROPTEST_CASES: 10
+  #   steps:
+  #     - uses: actions/checkout@v4
+  #     - uses: ilammy/setup-nasm@v1
+  #     - name: Install Rust ${{ env.rust_nightly }}
+  #       uses: dtolnay/rust-toolchain@master
+  #       with:
+  #         toolchain: ${{ env.rust_nightly }}
+  #         components: miri
+  #     - uses: Swatinem/rust-cache@v2
+  #       with:
+  #         cache-all-crates: "true"
+  #         save-if: ${{ github.ref == 'refs/heads/master' }}
+  #     - name: miri
+  #       # Many of tests in tokio/tests and doctests use #[tokio::test] or
+  #       # #[tokio::main] that calls epoll_create1 that Miri does not support.
+  #       # run: cargo miri test --features full --lib --no-fail-fast
+  #       run: |
+  #         cargo miri test --package dioxus-core -- --exact --nocapture
+  #         cargo miri test --package dioxus-native-core --test miri_native  -- --exact --nocapture
+  #       env:
+  #         MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
+  #         PROPTEST_CASES: 10
 
   playwright:
     if: github.event.pull_request.draft == false
@@ -160,6 +161,12 @@ jobs:
       - uses: actions/setup-node@v4
         with:
           node-version: 16
+      - name: Free Disk Space (Ubuntu)
+        uses: jlumbroso/free-disk-space@v1.3.1
+        with: # speed things up a bit
+          large-packages: false
+          docker-images: false
+          swap-storage: false
       - name: Install Rust
         uses: dtolnay/rust-toolchain@master
         with:
@@ -172,19 +179,19 @@ jobs:
 
       - name: Install dependencies
         run: npm ci
-        working-directory: ./playwright-tests
+        working-directory: ./packages/playwright-tests
 
       - name: Install Playwright
         run: npm install -D @playwright/test
-        working-directory: ./playwright-tests
+        working-directory: ./packages/playwright-tests
 
       - name: Install Playwright Browsers
         run: npx playwright install --with-deps
-        working-directory: ./playwright-tests
+        working-directory: ./packages/playwright-tests
 
       - name: Run Playwright tests
         run: npx playwright test
-        working-directory: ./playwright-tests
+        working-directory: ./packages/playwright-tests
 
       - uses: actions/upload-artifact@v4
         if: always()
@@ -205,7 +212,7 @@ jobs:
           - {
               target: x86_64-pc-windows-msvc,
               os: windows-latest,
-              toolchain: "1.70.0",
+              toolchain: "1.75.0",
               cross: false,
               command: "test",
               args: "--all --tests",
@@ -213,7 +220,7 @@ jobs:
           - {
               target: x86_64-apple-darwin,
               os: macos-latest,
-              toolchain: "1.70.0",
+              toolchain: "1.75.0",
               cross: false,
               command: "test",
               args: "--all --tests",
@@ -221,7 +228,7 @@ jobs:
           - {
               target: aarch64-apple-ios,
               os: macos-latest,
-              toolchain: "1.70.0",
+              toolchain: "1.75.0",
               cross: false,
               command: "build",
               args: "--package dioxus-mobile",
@@ -229,7 +236,7 @@ jobs:
           - {
               target: aarch64-linux-android,
               os: ubuntu-latest,
-              toolchain: "1.70.0",
+              toolchain: "1.75.0",
               cross: true,
               command: "build",
               args: "--package dioxus-mobile",
@@ -250,6 +257,14 @@ jobs:
 
         uses: taiki-e/install-action@cross
 
+      - name: Free Disk Space (Ubuntu)
+        if: ${{ matrix.platform.os == 'ubuntu-latest' }}
+        uses: jlumbroso/free-disk-space@v1.3.1
+        with: # speed things up a bit
+          large-packages: false
+          docker-images: false
+          swap-storage: false
+
       - uses: Swatinem/rust-cache@v2
         with:
           key: "${{ matrix.platform.target }}"

+ 4 - 4
.gitignore

@@ -1,6 +1,6 @@
 /target
-/playwright-tests/web/dist
-/playwright-tests/fullstack/dist
+/packages/playwright-tests/web/dist
+/packages/playwright-tests/fullstack/dist
 /dist
 .DS_Store
 /examples/assets/test_video.mp4
@@ -19,5 +19,5 @@ tarpaulin-report.html
 .idea/
 node_modules/
 /test-results/
-/playwright-report/
-/playwright/.cache/
+/packages/playwright-report/
+/packages/playwright/.cache/

+ 473 - 946
Cargo.lock

@@ -154,9 +154,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
 
 [[package]]
 name = "anstream"
-version = "0.6.5"
+version = "0.6.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
+checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -168,9 +168,9 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.4"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
 
 [[package]]
 name = "anstyle-parse"
@@ -324,17 +324,6 @@ dependencies = [
  "futures-core",
 ]
 
-[[package]]
-name = "async-channel"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
-dependencies = [
- "concurrent-queue",
- "event-listener 2.5.3",
- "futures-core",
-]
-
 [[package]]
 name = "async-channel"
 version = "2.1.1"
@@ -364,9 +353,9 @@ dependencies = [
 
 [[package]]
 name = "async-compression"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5"
+checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c"
 dependencies = [
  "flate2",
  "futures-core",
@@ -423,9 +412,9 @@ dependencies = [
 
 [[package]]
 name = "async-io"
-version = "2.2.2"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7"
+checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65"
 dependencies = [
  "async-lock 3.3.0",
  "cfg-if",
@@ -433,8 +422,8 @@ dependencies = [
  "futures-io",
  "futures-lite 2.2.0",
  "parking",
- "polling 3.3.1",
- "rustix 0.38.28",
+ "polling 3.4.0",
+ "rustix 0.38.31",
  "slab",
  "tracing",
  "windows-sys 0.52.0",
@@ -473,7 +462,7 @@ dependencies = [
  "cfg-if",
  "event-listener 3.1.0",
  "futures-lite 1.13.0",
- "rustix 0.38.28",
+ "rustix 0.38.31",
  "windows-sys 0.48.0",
 ]
 
@@ -494,13 +483,13 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
 dependencies = [
- "async-io 2.2.2",
+ "async-io 2.3.1",
  "async-lock 2.8.0",
  "atomic-waker",
  "cfg-if",
  "futures-core",
  "futures-io",
- "rustix 0.38.28",
+ "rustix 0.38.31",
  "signal-hook-registry",
  "slab",
  "windows-sys 0.48.0",
@@ -792,9 +781,6 @@ version = "0.1.0"
 dependencies = [
  "axum 0.6.20",
  "dioxus",
- "dioxus-desktop",
- "dioxus-fullstack",
- "dioxus-router",
  "serde",
  "tokio",
 ]
@@ -804,7 +790,6 @@ name = "axum-hello-world"
 version = "0.1.0"
 dependencies = [
  "dioxus",
- "dioxus-fullstack",
  "reqwest",
  "serde",
  "simple_logger",
@@ -831,9 +816,6 @@ version = "0.1.0"
 dependencies = [
  "axum 0.6.20",
  "dioxus",
- "dioxus-fullstack",
- "dioxus-router",
- "dioxus-web",
  "serde",
  "tokio",
 ]
@@ -925,22 +907,6 @@ dependencies = [
  "rustc-demangle",
 ]
 
-[[package]]
-name = "base16ct"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
-
-[[package]]
-name = "base64"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
-dependencies = [
- "byteorder",
- "safemem",
-]
-
 [[package]]
 name = "base64"
 version = "0.13.1"
@@ -1023,22 +989,13 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.4.1"
+version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
 dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "bitmaps"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
-dependencies = [
- "typenum",
-]
-
 [[package]]
 name = "bitstream-io"
 version = "1.10.0"
@@ -1084,7 +1041,7 @@ version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118"
 dependencies = [
- "async-channel 2.1.1",
+ "async-channel",
  "async-lock 3.3.0",
  "async-task",
  "fastrand 2.0.1",
@@ -1096,9 +1053,9 @@ dependencies = [
 
 [[package]]
 name = "borsh"
-version = "1.3.0"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d4d6dafc1a3bb54687538972158f07b2c948bc57d5890df22c0739098b3028"
+checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667"
 dependencies = [
  "borsh-derive",
  "cfg_aliases",
@@ -1106,12 +1063,12 @@ dependencies = [
 
 [[package]]
 name = "borsh-derive"
-version = "1.3.0"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0"
+checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd"
 dependencies = [
  "once_cell",
- "proc-macro-crate 2.0.0",
+ "proc-macro-crate 3.1.0",
  "proc-macro2",
  "quote",
  "syn 2.0.48",
@@ -1155,7 +1112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
 dependencies = [
  "memchr",
- "regex-automata 0.4.3",
+ "regex-automata 0.4.5",
  "serde",
 ]
 
@@ -1191,9 +1148,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
 
 [[package]]
 name = "bytecheck"
-version = "0.6.11"
+version = "0.6.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627"
+checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
 dependencies = [
  "bytecheck_derive",
  "ptr_meta",
@@ -1202,9 +1159,9 @@ dependencies = [
 
 [[package]]
 name = "bytecheck_derive"
-version = "0.6.11"
+version = "0.6.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61"
+checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1213,9 +1170,9 @@ dependencies = [
 
 [[package]]
 name = "bytemuck"
-version = "1.14.0"
+version = "1.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
+checksum = "ea31d69bda4949c1c1562c1e6f042a1caefac98cdc8a298260a2ff41c1e2d42b"
 
 [[package]]
 name = "byteorder"
@@ -1256,7 +1213,7 @@ version = "0.18.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "cairo-sys-rs",
  "glib",
  "libc",
@@ -1292,7 +1249,7 @@ checksum = "92c1b6f44358912a9538fa3b6ac8d3aa3f585444f9dc32f12ed85d1545a9df9f"
 dependencies = [
  "anyhow",
  "auth-git2",
- "clap 4.4.15",
+ "clap 4.4.18",
  "console",
  "dialoguer",
  "env_logger",
@@ -1302,7 +1259,7 @@ dependencies = [
  "heck 0.4.1",
  "home",
  "ignore",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "indicatif",
  "liquid",
  "liquid-core",
@@ -1320,7 +1277,7 @@ dependencies = [
  "serde",
  "tempfile",
  "thiserror",
- "toml 0.8.8",
+ "toml 0.8.10",
  "walkdir",
 ]
 
@@ -1407,7 +1364,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3"
 dependencies = [
  "serde",
- "toml 0.8.8",
+ "toml 0.8.10",
 ]
 
 [[package]]
@@ -1417,7 +1374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "802b755090e39835a4b0440fb0bbee0df7495a8b337f63db21e616f7821c7e8c"
 dependencies = [
  "serde",
- "toml 0.8.8",
+ "toml 0.8.10",
 ]
 
 [[package]]
@@ -1492,9 +1449,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
 
 [[package]]
 name = "chrono"
-version = "0.4.31"
+version = "0.4.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -1502,14 +1459,14 @@ dependencies = [
  "num-traits",
  "serde",
  "wasm-bindgen",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.0",
 ]
 
 [[package]]
 name = "ciborium"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
 dependencies = [
  "ciborium-io",
  "ciborium-ll",
@@ -1518,18 +1475,18 @@ dependencies = [
 
 [[package]]
 name = "ciborium-io"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
 
 [[package]]
 name = "ciborium-ll"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
 dependencies = [
  "ciborium-io",
- "half 1.8.2",
+ "half 2.3.1",
 ]
 
 [[package]]
@@ -1555,9 +1512,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.4.15"
+version = "4.4.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c12ed66a79a555082f595f7eb980d08669de95009dd4b3d61168c573ebe38fc9"
+checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -1565,9 +1522,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.4.15"
+version = "4.4.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f4645eab3431e5a8403a96bea02506a8b35d28cd0f0330977dd5d22f9c84f43"
+checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
 dependencies = [
  "anstream",
  "anstyle",
@@ -1593,12 +1550,6 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
 
-[[package]]
-name = "closure"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6173fd61b610d15a7566dd7b7620775627441c4ab9dac8906e17cb93a24b782"
-
 [[package]]
 name = "cocoa"
 version = "0.25.0"
@@ -1943,7 +1894,7 @@ dependencies = [
  "anes",
  "cast",
  "ciborium",
- "clap 4.4.15",
+ "clap 4.4.18",
  "criterion-plot 0.5.0",
  "futures",
  "is-terminal",
@@ -2047,7 +1998,7 @@ version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "crossterm_winapi",
  "libc",
  "mio",
@@ -2082,18 +2033,6 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
 
-[[package]]
-name = "crypto-bigint"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
-dependencies = [
- "generic-array 0.14.7",
- "rand_core 0.6.4",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "crypto-common"
 version = "0.1.6"
@@ -2122,23 +2061,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "cssparser"
-version = "0.29.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa"
-dependencies = [
- "cssparser-macros",
- "dtoa-short",
- "itoa 1.0.10",
- "matches",
- "phf 0.10.1",
- "proc-macro2",
- "quote",
- "smallvec",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "cssparser"
 version = "0.33.0"
@@ -2221,34 +2143,6 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "curve25519-dalek"
-version = "4.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "curve25519-dalek-derive",
- "digest",
- "fiat-crypto",
- "platforms",
- "rustc_version",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "curve25519-dalek-derive"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.48",
-]
-
 [[package]]
 name = "cvt"
 version = "0.1.2"
@@ -2260,9 +2154,9 @@ dependencies = [
 
 [[package]]
 name = "darling"
-version = "0.20.3"
+version = "0.20.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
+checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8"
 dependencies = [
  "darling_core",
  "darling_macro",
@@ -2270,9 +2164,9 @@ dependencies = [
 
 [[package]]
 name = "darling_core"
-version = "0.20.3"
+version = "0.20.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
+checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3"
 dependencies = [
  "fnv",
  "ident_case",
@@ -2284,9 +2178,9 @@ dependencies = [
 
 [[package]]
 name = "darling_macro"
-version = "0.20.3"
+version = "0.20.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
+checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
 dependencies = [
  "darling_core",
  "quote",
@@ -2392,7 +2286,7 @@ version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "proc-macro2",
  "proc-macro2-diagnostics",
  "quote",
@@ -2435,15 +2329,25 @@ name = "dioxus"
 version = "0.4.3"
 dependencies = [
  "criterion 0.3.6",
+ "dioxus-config-macro",
  "dioxus-core",
  "dioxus-core-macro",
+ "dioxus-desktop",
+ "dioxus-fullstack",
  "dioxus-hooks",
  "dioxus-hot-reload",
  "dioxus-html",
- "dioxus-rsx",
+ "dioxus-liveview",
+ "dioxus-mobile",
+ "dioxus-router",
+ "dioxus-signals",
+ "dioxus-ssr",
+ "dioxus-tui",
+ "dioxus-web",
  "env_logger",
  "futures-util",
  "rand 0.8.5",
+ "serde",
  "thiserror",
  "tokio",
  "tracing",
@@ -2486,7 +2390,7 @@ dependencies = [
  "cargo_metadata 0.18.1",
  "cargo_toml 0.18.0",
  "chrono",
- "clap 4.4.15",
+ "clap 4.4.18",
  "colored 2.1.0",
  "ctrlc",
  "dioxus-autofmt",
@@ -2525,8 +2429,8 @@ dependencies = [
  "tempfile",
  "thiserror",
  "tokio",
- "toml 0.8.8",
- "toml_edit 0.21.0",
+ "toml 0.8.10",
+ "toml_edit 0.21.1",
  "tower",
  "tower-http 0.2.5",
  "walkdir",
@@ -2539,7 +2443,7 @@ name = "dioxus-cli-config"
 version = "0.4.1"
 dependencies = [
  "cargo_toml 0.16.3",
- "clap 4.4.15",
+ "clap 4.4.18",
  "once_cell",
  "serde",
  "serde_json",
@@ -2549,13 +2453,19 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "dioxus-config-macro"
+version = "0.4.3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
 [[package]]
 name = "dioxus-core"
 version = "0.4.3"
 dependencies = [
- "bumpalo",
  "dioxus",
- "dioxus-html",
  "dioxus-ssr",
  "futures-channel",
  "futures-util",
@@ -2565,10 +2475,8 @@ dependencies = [
  "rustc-hash",
  "serde",
  "slab",
- "smallbox",
  "tokio",
  "tracing",
- "trybuild",
 ]
 
 [[package]]
@@ -2578,7 +2486,6 @@ dependencies = [
  "constcat",
  "convert_case 0.6.0",
  "dioxus",
- "dioxus-core",
  "dioxus-rsx",
  "prettyplease",
  "proc-macro2",
@@ -2604,7 +2511,6 @@ dependencies = [
  "dioxus",
  "dioxus-cli-config",
  "dioxus-core",
- "dioxus-core-macro",
  "dioxus-hooks",
  "dioxus-hot-reload",
  "dioxus-html",
@@ -2613,15 +2519,14 @@ dependencies = [
  "exitcode",
  "futures-channel",
  "futures-util",
+ "generational-box",
  "global-hotkey",
  "infer 0.11.0",
- "minify-js",
  "muda",
  "objc",
  "objc_id",
  "rfd",
  "rustc-hash",
- "scraper",
  "serde",
  "serde_json",
  "slab",
@@ -2638,29 +2543,19 @@ dependencies = [
 name = "dioxus-examples"
 version = "0.4.3"
 dependencies = [
- "anyhow",
  "dioxus",
- "dioxus-desktop",
- "dioxus-router",
- "dioxus-signals",
  "dioxus-ssr",
- "env_logger",
- "fermi",
+ "form_urlencoded",
  "futures-util",
+ "getrandom 0.2.12",
  "http-range",
- "im-rc",
- "log",
  "manganis",
- "num-format",
  "rand 0.8.5",
  "reqwest",
  "separator",
  "serde",
  "serde_json",
- "simple_logger",
- "thiserror",
  "tokio",
- "tracing-subscriber",
 ]
 
 [[package]]
@@ -2683,10 +2578,11 @@ dependencies = [
  "base64 0.21.7",
  "bytes",
  "ciborium",
- "dioxus",
+ "dioxus-cli-config",
  "dioxus-desktop",
  "dioxus-hot-reload",
- "dioxus-router",
+ "dioxus-lib",
+ "dioxus-mobile",
  "dioxus-ssr",
  "dioxus-web",
  "dioxus_server_macro",
@@ -2720,6 +2616,7 @@ dependencies = [
  "dioxus",
  "dioxus-core",
  "dioxus-debug-cell",
+ "dioxus-signals",
  "futures-channel",
  "futures-util",
  "slab",
@@ -2749,13 +2646,14 @@ dependencies = [
 name = "dioxus-html"
 version = "0.4.3"
 dependencies = [
- "async-channel 1.9.0",
  "async-trait",
  "dioxus-core",
  "dioxus-html-internal-macro",
  "dioxus-rsx",
  "enumset",
  "euclid",
+ "futures-channel",
+ "generational-box",
  "keyboard-types",
  "rfd",
  "serde",
@@ -2782,6 +2680,8 @@ dependencies = [
 name = "dioxus-interpreter-js"
 version = "0.4.3"
 dependencies = [
+ "dioxus-core",
+ "dioxus-html",
  "js-sys",
  "serde",
  "sledgehammer_bindgen",
@@ -2790,6 +2690,19 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "dioxus-lib"
+version = "0.4.3"
+dependencies = [
+ "dioxus-config-macro",
+ "dioxus-core",
+ "dioxus-core-macro",
+ "dioxus-hooks",
+ "dioxus-html",
+ "dioxus-rsx",
+ "dioxus-signals",
+]
+
 [[package]]
 name = "dioxus-liveview"
 version = "0.4.3"
@@ -2797,12 +2710,14 @@ dependencies = [
  "async-trait",
  "axum 0.6.20",
  "dioxus",
+ "dioxus-cli-config",
  "dioxus-core",
  "dioxus-hot-reload",
  "dioxus-html",
  "dioxus-interpreter-js",
  "futures-channel",
  "futures-util",
+ "generational-box",
  "minify-js",
  "once_cell",
  "pretty_env_logger",
@@ -2822,16 +2737,6 @@ dependencies = [
  "warp",
 ]
 
-[[package]]
-name = "dioxus-logger"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d7cbab0b5519060fe9e14b3c21e3f2329b8386cd905618f78c7b929cd00cf54"
-dependencies = [
- "log",
- "web-sys",
-]
-
 [[package]]
 name = "dioxus-mobile"
 version = "0.4.3"
@@ -2877,13 +2782,8 @@ dependencies = [
 name = "dioxus-playwright-fullstack-test"
 version = "0.1.0"
 dependencies = [
- "axum 0.6.20",
  "dioxus",
- "dioxus-fullstack",
- "dioxus-web",
- "execute",
  "serde",
- "tokio",
 ]
 
 [[package]]
@@ -2901,9 +2801,9 @@ name = "dioxus-playwright-web-test"
 version = "0.0.1"
 dependencies = [
  "dioxus",
- "dioxus-html",
- "dioxus-web",
  "serde_json",
+ "tracing",
+ "tracing-wasm",
 ]
 
 [[package]]
@@ -2926,14 +2826,14 @@ dependencies = [
  "criterion 0.5.1",
  "dioxus",
  "dioxus-cli-config",
- "dioxus-desktop",
+ "dioxus-fullstack",
+ "dioxus-lib",
  "dioxus-liveview",
  "dioxus-router",
  "dioxus-router-macro",
  "dioxus-ssr",
- "dioxus-web",
  "gloo",
- "gloo-utils 0.1.7",
+ "gloo-utils",
  "js-sys",
  "serde",
  "serde_json",
@@ -2943,7 +2843,6 @@ dependencies = [
  "urlencoding",
  "wasm-bindgen",
  "wasm-bindgen-test",
- "wasm-logger",
  "web-sys",
 ]
 
@@ -2977,12 +2876,18 @@ version = "0.4.3"
 dependencies = [
  "dioxus",
  "dioxus-core",
- "dioxus-desktop",
+ "flume",
+ "futures-channel",
+ "futures-util",
  "generational-box",
+ "once_cell",
+ "parking_lot",
+ "rustc-hash",
  "serde",
  "simple_logger",
  "tokio",
  "tracing",
+ "tracing-subscriber",
 ]
 
 [[package]]
@@ -2996,8 +2901,10 @@ dependencies = [
  "dioxus",
  "dioxus-core",
  "dioxus-html",
+ "dioxus-signals",
  "fern",
  "fs_extra",
+ "generational-box",
  "http 0.2.11",
  "lru 0.10.1",
  "rustc-hash",
@@ -3013,8 +2920,6 @@ name = "dioxus-tailwind"
 version = "0.0.0"
 dependencies = [
  "dioxus",
- "dioxus-desktop",
- "dioxus-web",
  "manganis",
 ]
 
@@ -3040,7 +2945,6 @@ dependencies = [
 name = "dioxus-web"
 version = "0.4.3"
 dependencies = [
- "async-channel 1.9.0",
  "async-trait",
  "console_error_panic_hook",
  "dioxus",
@@ -3051,6 +2955,7 @@ dependencies = [
  "dioxus-web",
  "futures-channel",
  "futures-util",
+ "generational-box",
  "gloo-dialogs",
  "gloo-timers",
  "js-sys",
@@ -3063,7 +2968,6 @@ dependencies = [
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "wasm-bindgen-test",
- "wasm-logger",
  "web-sys",
 ]
 
@@ -3165,56 +3069,6 @@ version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
 
-[[package]]
-name = "dyn-clone"
-version = "1.0.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
-
-[[package]]
-name = "ecdsa"
-version = "0.16.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
-dependencies = [
- "der",
- "digest",
- "elliptic-curve",
- "rfc6979",
- "signature",
- "spki",
-]
-
-[[package]]
-name = "ed25519"
-version = "2.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
-dependencies = [
- "pkcs8",
- "signature",
-]
-
-[[package]]
-name = "ed25519-dalek"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
-dependencies = [
- "curve25519-dalek",
- "ed25519",
- "serde",
- "sha2",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "ego-tree"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
-
 [[package]]
 name = "either"
 version = "1.9.0"
@@ -3224,27 +3078,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "elliptic-curve"
-version = "0.13.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
-dependencies = [
- "base16ct",
- "crypto-bigint",
- "digest",
- "ff",
- "generic-array 0.14.7",
- "group",
- "hkdf",
- "pem-rfc7468",
- "pkcs8",
- "rand_core 0.6.4",
- "sec1",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "encode_unicode"
 version = "0.3.6"
@@ -3316,9 +3149,9 @@ dependencies = [
 
 [[package]]
 name = "env_logger"
-version = "0.10.1"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
+checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
 dependencies = [
  "humantime",
  "is-terminal",
@@ -3457,13 +3290,13 @@ checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
 
 [[package]]
 name = "exr"
-version = "1.71.0"
+version = "1.72.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
+checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
 dependencies = [
  "bit_field",
  "flume",
- "half 2.2.1",
+ "half 2.3.1",
  "lebe",
  "miniz_oxide",
  "rayon-core",
@@ -3503,23 +3336,13 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
 
 [[package]]
 name = "fdeflate"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd"
+checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
 dependencies = [
  "simd-adler32",
 ]
 
-[[package]]
-name = "fermi"
-version = "0.4.3"
-dependencies = [
- "closure",
- "dioxus-core",
- "im-rc",
- "tracing",
-]
-
 [[package]]
 name = "fern"
 version = "0.6.2"
@@ -3530,22 +3353,6 @@ dependencies = [
  "log",
 ]
 
-[[package]]
-name = "ff"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
-dependencies = [
- "rand_core 0.6.4",
- "subtle",
-]
-
-[[package]]
-name = "fiat-crypto"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
-
 [[package]]
 name = "field-offset"
 version = "0.3.6"
@@ -3558,14 +3365,14 @@ dependencies = [
 
 [[package]]
 name = "figment"
-version = "0.10.13"
+version = "0.10.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7629b8c7bcd214a072c2c88b263b5bb3ceb54c34365d8c41c1665461aeae0993"
+checksum = "2b6e5bc7bd59d60d0d45a6ccab6cf0f4ce28698fb4e81e750ddf229c9b824026"
 dependencies = [
  "atomic 0.6.0",
  "pear",
  "serde",
- "toml 0.8.8",
+ "toml 0.8.10",
  "uncased",
  "version_check",
 ]
@@ -3966,7 +3773,8 @@ dependencies = [
 name = "generational-box"
 version = "0.4.3"
 dependencies = [
- "bumpalo",
+ "criterion 0.3.6",
+ "parking_lot",
  "rand 0.8.5",
 ]
 
@@ -3991,7 +3799,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
 dependencies = [
  "typenum",
  "version_check",
- "zeroize",
 ]
 
 [[package]]
@@ -4003,15 +3810,6 @@ dependencies = [
  "typenum",
 ]
 
-[[package]]
-name = "getopts"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
-dependencies = [
- "unicode-width",
-]
-
 [[package]]
 name = "getrandom"
 version = "0.1.16"
@@ -4107,11 +3905,11 @@ dependencies = [
 
 [[package]]
 name = "git2"
-version = "0.18.1"
+version = "0.18.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd"
+checksum = "1b3ba52851e73b46a4c3df1d89343741112003f0f6f13beb0dfac9e457c3fdcd"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "libc",
  "libgit2-sys",
  "log",
@@ -4157,11 +3955,11 @@ dependencies = [
 
 [[package]]
 name = "gix-config-value"
-version = "0.14.3"
+version = "0.14.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52e0be46f4cf1f8f9e88d0e3eb7b29718aff23889563249f379119bd1ab6910e"
+checksum = "5b8a1e7bfb37a46ed0b8468db37a6d8a0a61d56bdbe4603ae492cb322e5f3958"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "bstr 1.9.0",
  "gix-path",
  "libc",
@@ -4209,7 +4007,7 @@ version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5db19298c5eeea2961e5b3bf190767a2d1f09b8802aeb5f258e42276350aff19"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "bstr 1.9.0",
  "gix-features",
  "gix-path",
@@ -4257,9 +4055,9 @@ dependencies = [
 
 [[package]]
 name = "gix-path"
-version = "0.10.3"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8dd0998ab245f33d40ca2267e58d542fe54185ebd1dc41923346cf28d179fb6"
+checksum = "97e9ad649bf5e109562d6acba657ca428661ec08e77eaf3a755d8fa55485be9c"
 dependencies = [
  "bstr 1.9.0",
  "gix-trace",
@@ -4291,14 +4089,14 @@ dependencies = [
 
 [[package]]
 name = "gix-sec"
-version = "0.10.3"
+version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78f6dce0c6683e2219e8169aac4b1c29e89540a8262fef7056b31d80d969408c"
+checksum = "f8d9bf462feaf05f2121cba7399dbc6c34d88a9cad58fc1e95027791d6a3c6d2"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "gix-path",
  "libc",
- "windows 0.52.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -4316,17 +4114,18 @@ dependencies = [
 
 [[package]]
 name = "gix-trace"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8e1127ede0475b58f4fe9c0aaa0d9bb0bad2af90bbd93ccd307c8632b863d89"
+checksum = "02b202d766a7fefc596e2cc6a89cda8ad8ad733aed82da635ac120691112a9b1"
 
 [[package]]
 name = "gix-utils"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de6225e2de30b6e9bca2d9f1cc4731640fcef0fb3cabddceee366e7e85d3e94f"
+checksum = "56e839f3d0798b296411263da6bee780a176ef8008a5dfc31287f7eda9266ab8"
 dependencies = [
  "fastrand 2.0.1",
+ "unicode-normalization",
 ]
 
 [[package]]
@@ -4345,7 +4144,7 @@ version = "0.18.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "futures-channel",
  "futures-core",
  "futures-executor",
@@ -4394,9 +4193,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "global-hotkey"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d505f007b733fc3b3261ca0710e7e689411422ace5f5b2a63a14d8596159db23"
+checksum = "927a00fd7c31d82029f99ce2481a8de1ae974758017d6a55ebbe7f22edcd1617"
 dependencies = [
  "crossbeam-channel",
  "keyboard-types",
@@ -4415,7 +4214,7 @@ dependencies = [
  "aho-corasick 1.1.2",
  "bstr 1.9.0",
  "log",
- "regex-automata 0.4.3",
+ "regex-automata 0.4.5",
  "regex-syntax 0.8.2",
 ]
 
@@ -4432,9 +4231,9 @@ dependencies = [
  "gloo-history",
  "gloo-net 0.3.1",
  "gloo-render",
- "gloo-storage 0.2.2",
+ "gloo-storage",
  "gloo-timers",
- "gloo-utils 0.1.7",
+ "gloo-utils",
  "gloo-worker",
 ]
 
@@ -4444,7 +4243,7 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
 dependencies = [
- "gloo-utils 0.1.7",
+ "gloo-utils",
  "js-sys",
  "serde",
  "wasm-bindgen",
@@ -4490,7 +4289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f"
 dependencies = [
  "gloo-events",
- "gloo-utils 0.1.7",
+ "gloo-utils",
  "serde",
  "serde-wasm-bindgen",
  "serde_urlencoded",
@@ -4508,7 +4307,7 @@ dependencies = [
  "futures-channel",
  "futures-core",
  "futures-sink",
- "gloo-utils 0.1.7",
+ "gloo-utils",
  "js-sys",
  "pin-project",
  "serde",
@@ -4528,7 +4327,7 @@ dependencies = [
  "futures-channel",
  "futures-core",
  "futures-sink",
- "gloo-utils 0.1.7",
+ "gloo-utils",
  "http 0.2.11",
  "js-sys",
  "pin-project",
@@ -4556,22 +4355,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
 dependencies = [
- "gloo-utils 0.1.7",
- "js-sys",
- "serde",
- "serde_json",
- "thiserror",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "gloo-storage"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a"
-dependencies = [
- "gloo-utils 0.2.0",
+ "gloo-utils",
  "js-sys",
  "serde",
  "serde_json",
@@ -4603,19 +4387,6 @@ dependencies = [
  "web-sys",
 ]
 
-[[package]]
-name = "gloo-utils"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
-dependencies = [
- "js-sys",
- "serde",
- "serde_json",
- "wasm-bindgen",
- "web-sys",
-]
-
 [[package]]
 name = "gloo-worker"
 version = "0.2.1"
@@ -4625,7 +4396,7 @@ dependencies = [
  "anymap2",
  "bincode",
  "gloo-console",
- "gloo-utils 0.1.7",
+ "gloo-utils",
  "js-sys",
  "serde",
  "wasm-bindgen",
@@ -4650,17 +4421,6 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c"
 
-[[package]]
-name = "group"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
-dependencies = [
- "ff",
- "rand_core 0.6.4",
- "subtle",
-]
-
 [[package]]
 name = "gtk"
 version = "0.18.1"
@@ -4725,7 +4485,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http 0.2.11",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "slab",
  "tokio",
  "tokio-util",
@@ -4734,9 +4494,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "991910e35c615d8cab86b5ab04be67e6ad24d2bf5f4f11fdbbed26da999bbeab"
+checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943"
 dependencies = [
  "bytes",
  "fnv",
@@ -4744,7 +4504,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http 1.0.0",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "slab",
  "tokio",
  "tokio-util",
@@ -4759,10 +4519,11 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
 
 [[package]]
 name = "half"
-version = "2.2.1"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
+checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
 dependencies = [
+ "cfg-if",
  "crunchy",
 ]
 
@@ -4895,9 +4656,9 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.3.3"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
 
 [[package]]
 name = "hex"
@@ -5091,7 +4852,7 @@ dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
- "h2 0.4.1",
+ "h2 0.4.2",
  "http 1.0.0",
  "http-body 1.0.0",
  "httparse",
@@ -5146,12 +4907,11 @@ dependencies = [
 
 [[package]]
 name = "hyper-util"
-version = "0.1.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67"
+checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
 dependencies = [
  "bytes",
- "futures-channel",
  "futures-util",
  "http 1.0.0",
  "http-body 1.0.0",
@@ -5159,14 +4919,13 @@ dependencies = [
  "pin-project-lite",
  "socket2 0.5.5",
  "tokio",
- "tracing",
 ]
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.59"
+version = "0.1.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
@@ -5227,32 +4986,17 @@ dependencies = [
  "globset",
  "log",
  "memchr",
- "regex-automata 0.4.3",
+ "regex-automata 0.4.5",
  "same-file",
  "walkdir",
  "winapi-util",
 ]
 
-[[package]]
-name = "im-rc"
-version = "15.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
-dependencies = [
- "bitmaps",
- "rand_core 0.6.4",
- "rand_xoshiro",
- "serde",
- "sized-chunks",
- "typenum",
- "version_check",
-]
-
 [[package]]
 name = "image"
-version = "0.24.7"
+version = "0.24.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
+checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23"
 dependencies = [
  "bytemuck",
  "byteorder",
@@ -5260,7 +5004,6 @@ dependencies = [
  "exr",
  "gif",
  "jpeg-decoder",
- "num-rational",
  "num-traits",
  "png",
  "qoi",
@@ -5272,9 +5015,9 @@ dependencies = [
 
 [[package]]
 name = "imagequant"
-version = "4.2.2"
+version = "4.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84d51957ac48371e8e2eaaeb4eba56150ff2109c1c8c200002afb7dd6e2d260f"
+checksum = "85a7f142d232ccbdc00cbef49d17f45639aeb07d9bfe28e17c21dea3efac64e5"
 dependencies = [
  "arrayvec",
  "once_cell",
@@ -5285,9 +5028,9 @@ dependencies = [
 
 [[package]]
 name = "imgref"
-version = "1.10.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90d944e334f00f4449c9640b440a171f816be0152305c12ef90424fc35fd035c"
+checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
 
 [[package]]
 name = "indexmap"
@@ -5302,9 +5045,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.1.0"
+version = "2.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.3",
@@ -5441,9 +5184,9 @@ checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9"
 
 [[package]]
 name = "inventory"
-version = "0.3.14"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8573b2b1fb643a372c73b23f4da5f888677feef3305146d68a539250a9bccc7"
+checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767"
 
 [[package]]
 name = "io-lifetimes"
@@ -5451,7 +5194,7 @@ version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
 dependencies = [
- "hermit-abi 0.3.3",
+ "hermit-abi 0.3.5",
  "libc",
  "windows-sys 0.48.0",
 ]
@@ -5507,8 +5250,8 @@ version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
 dependencies = [
- "hermit-abi 0.3.3",
- "rustix 0.38.28",
+ "hermit-abi 0.3.5",
+ "rustix 0.38.31",
  "windows-sys 0.52.0",
 ]
 
@@ -5524,9 +5267,9 @@ dependencies = [
 
 [[package]]
 name = "is_ci"
-version = "1.1.1"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
+checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
 
 [[package]]
 name = "itertools"
@@ -5548,9 +5291,9 @@ dependencies = [
 
 [[package]]
 name = "itertools"
-version = "0.12.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
 dependencies = [
  "either",
 ]
@@ -5623,18 +5366,18 @@ dependencies = [
 
 [[package]]
 name = "jpeg-decoder"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
+checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
 dependencies = [
  "rayon",
 ]
 
 [[package]]
 name = "js-sys"
-version = "0.3.66"
+version = "0.3.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
+checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -5657,7 +5400,7 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "serde",
  "unicode-segmentation",
 ]
@@ -5714,7 +5457,7 @@ dependencies = [
  "html5ever",
  "indexmap 1.9.3",
  "matches",
- "selectors 0.22.0",
+ "selectors",
 ]
 
 [[package]]
@@ -5740,9 +5483,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
 
 [[package]]
 name = "libc"
-version = "0.2.152"
+version = "0.2.153"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
 
 [[package]]
 name = "libflate"
@@ -5787,9 +5530,9 @@ dependencies = [
 
 [[package]]
 name = "libgit2-sys"
-version = "0.16.1+1.7.1"
+version = "0.16.2+1.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c"
+checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8"
 dependencies = [
  "cc",
  "libc",
@@ -5811,7 +5554,7 @@ version = "0.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "libc",
  "redox_syscall",
 ]
@@ -5872,9 +5615,9 @@ dependencies = [
 
 [[package]]
 name = "libz-sys"
-version = "1.1.14"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "295c17e837573c8c821dbaeb3cceb3d745ad082f7572191409e69cbc1b3fd050"
+checksum = "037731f5d3aaa87a5675e895b63ddff1a87624bc29f77004ea829809654e48f6"
 dependencies = [
  "cc",
  "libc",
@@ -5884,12 +5627,12 @@ dependencies = [
 
 [[package]]
 name = "lightningcss"
-version = "1.0.0-alpha.51"
+version = "1.0.0-alpha.52"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99d6ad516c08b24c246b339159dc2ee2144c012e8ebdf4db4bddefb8734b2b69"
+checksum = "771a62dedf5ec563bbfea9760f6c6a6bc546e67355eba0cd7d00c0dc34b11d90"
 dependencies = [
  "ahash 0.7.7",
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "const-str",
  "cssparser 0.33.0",
  "cssparser-color",
@@ -5935,9 +5678,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.12"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
 [[package]]
 name = "liquid"
@@ -6038,9 +5781,9 @@ dependencies = [
 
 [[package]]
 name = "loop9"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81a837f917de41d61ab531ba255d1913208d02325cab0d6a66a706e0dbaa699d"
+checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
 dependencies = [
  "imgref",
 ]
@@ -6065,9 +5808,9 @@ dependencies = [
 
 [[package]]
 name = "lru"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7"
+checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22"
 dependencies = [
  "hashbrown 0.14.3",
 ]
@@ -6128,7 +5871,7 @@ dependencies = [
 [[package]]
 name = "manganis"
 version = "0.1.0"
-source = "git+https://github.com/DioxusLabs/collect-assets?rev=e0093a4#e0093a47f0fa3bb50c49cd21aee5aa674faa24ad"
+source = "git+https://github.com/DioxusLabs/collect-assets?rev=f982698#f982698027fc27d22acfb4121fba389ec125d538"
 dependencies = [
  "manganis-macro",
 ]
@@ -6136,7 +5879,7 @@ dependencies = [
 [[package]]
 name = "manganis-cli-support"
 version = "0.1.0"
-source = "git+https://github.com/DioxusLabs/collect-assets?rev=e0093a4#e0093a47f0fa3bb50c49cd21aee5aa674faa24ad"
+source = "git+https://github.com/DioxusLabs/collect-assets?rev=f982698#f982698027fc27d22acfb4121fba389ec125d538"
 dependencies = [
  "anyhow",
  "cargo-lock 9.0.0",
@@ -6162,7 +5905,7 @@ dependencies = [
 [[package]]
 name = "manganis-common"
 version = "0.1.0"
-source = "git+https://github.com/DioxusLabs/collect-assets?rev=e0093a4#e0093a47f0fa3bb50c49cd21aee5aa674faa24ad"
+source = "git+https://github.com/DioxusLabs/collect-assets?rev=f982698#f982698027fc27d22acfb4121fba389ec125d538"
 dependencies = [
  "anyhow",
  "base64 0.21.7",
@@ -6177,7 +5920,7 @@ dependencies = [
 [[package]]
 name = "manganis-macro"
 version = "0.0.1"
-source = "git+https://github.com/DioxusLabs/collect-assets?rev=e0093a4#e0093a47f0fa3bb50c49cd21aee5aa674faa24ad"
+source = "git+https://github.com/DioxusLabs/collect-assets?rev=f982698#f982698027fc27d22acfb4121fba389ec125d538"
 dependencies = [
  "base64 0.21.7",
  "manganis-cli-support",
@@ -6346,9 +6089,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
 dependencies = [
  "adler",
  "simd-adler32",
@@ -6427,9 +6170,9 @@ dependencies = [
 
 [[package]]
 name = "muda"
-version = "0.11.3"
+version = "0.11.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "884427679a3ef726cd4a0ad6eec2ca618737ba11ebe8c9320dc305c09c78c8fc"
+checksum = "e406691fa7749604bbc7964bde28a300572d52621bb84540f6907c0f8fe08737"
 dependencies = [
  "cocoa",
  "crossbeam-channel",
@@ -6592,7 +6335,7 @@ version = "0.27.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "cfg-if",
  "libc",
 ]
@@ -6685,6 +6428,12 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
 [[package]]
 name = "num-derive"
 version = "0.3.3"
@@ -6698,25 +6447,15 @@ dependencies = [
 
 [[package]]
 name = "num-derive"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
-[[package]]
-name = "num-format"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
-dependencies = [
- "arrayvec",
- "itoa 1.0.10",
-]
-
 [[package]]
 name = "num-integer"
 version = "0.1.45"
@@ -6766,7 +6505,7 @@ version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi 0.3.3",
+ "hermit-abi 0.3.5",
  "libc",
 ]
 
@@ -6791,41 +6530,21 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "num_threads"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "number_prefix"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
-
-[[package]]
-name = "oauth2"
-version = "4.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f"
-dependencies = [
- "base64 0.13.1",
- "chrono",
- "getrandom 0.2.12",
- "http 0.2.11",
- "rand 0.8.5",
- "reqwest",
- "serde",
- "serde_json",
- "serde_path_to_error",
- "sha2",
- "thiserror",
- "url",
+[[package]]
+name = "num_threads"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+dependencies = [
+ "libc",
 ]
 
+[[package]]
+name = "number_prefix"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
+
 [[package]]
 name = "objc"
 version = "0.2.7"
@@ -6903,67 +6622,13 @@ dependencies = [
  "pathdiff",
 ]
 
-[[package]]
-name = "openid_auth_demo"
-version = "0.1.0"
-dependencies = [
- "console_error_panic_hook",
- "dioxus",
- "dioxus-logger",
- "dioxus-router",
- "dioxus-web",
- "fermi",
- "form_urlencoded",
- "gloo-storage 0.3.0",
- "log",
- "openidconnect",
- "reqwest",
- "serde",
- "serde_json",
- "thiserror",
- "uuid",
- "web-sys",
-]
-
-[[package]]
-name = "openidconnect"
-version = "3.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62d6050f6a84b81f23c569f5607ad883293e57491036e318fafe6fc4895fadb1"
-dependencies = [
- "base64 0.13.1",
- "chrono",
- "dyn-clone",
- "ed25519-dalek",
- "hmac",
- "http 0.2.11",
- "itertools 0.10.5",
- "log",
- "oauth2",
- "p256",
- "p384",
- "rand 0.8.5",
- "rsa",
- "serde",
- "serde-value",
- "serde_derive",
- "serde_json",
- "serde_path_to_error",
- "serde_plain",
- "serde_with",
- "sha2",
- "subtle",
- "thiserror",
- "url",
-]
-
 [[package]]
 name = "openssl"
-version = "0.10.62"
+version = "0.10.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
+checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "cfg-if",
  "foreign-types 0.3.2",
  "libc",
@@ -6991,18 +6656,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "openssl-src"
-version = "300.2.1+3.2.0"
+version = "300.2.2+3.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3"
+checksum = "8bbfad0063610ac26ee79f7484739e2b07555a75c42453b89263830b5c8103bc"
 dependencies = [
  "cc",
 ]
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.98"
+version = "0.9.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
+checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae"
 dependencies = [
  "cc",
  "libc",
@@ -7067,30 +6732,6 @@ dependencies = [
  "supports-color",
 ]
 
-[[package]]
-name = "p256"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
-dependencies = [
- "ecdsa",
- "elliptic-curve",
- "primeorder",
- "sha2",
-]
-
-[[package]]
-name = "p384"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209"
-dependencies = [
- "ecdsa",
- "elliptic-curve",
- "primeorder",
- "sha2",
-]
-
 [[package]]
 name = "pango"
 version = "0.18.3"
@@ -7122,7 +6763,7 @@ version = "0.26.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "05d74befe2d076330d9a58bf9ca2da424568724ab278adf15fb5718253133887"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "cssparser 0.33.0",
  "fxhash",
  "log",
@@ -7287,9 +6928,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "pest"
-version = "2.7.6"
+version = "2.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06"
+checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546"
 dependencies = [
  "memchr",
  "thiserror",
@@ -7298,9 +6939,9 @@ dependencies = [
 
 [[package]]
 name = "pest_derive"
-version = "2.7.6"
+version = "2.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde"
+checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809"
 dependencies = [
  "pest",
  "pest_generator",
@@ -7308,9 +6949,9 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.7.6"
+version = "2.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275"
+checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e"
 dependencies = [
  "pest",
  "pest_meta",
@@ -7321,9 +6962,9 @@ dependencies = [
 
 [[package]]
 name = "pest_meta"
-version = "2.7.6"
+version = "2.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d"
+checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a"
 dependencies = [
  "once_cell",
  "pest",
@@ -7337,7 +6978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
 dependencies = [
  "fixedbitset",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
 ]
 
 [[package]]
@@ -7357,9 +6998,7 @@ version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
 dependencies = [
- "phf_macros 0.10.0",
  "phf_shared 0.10.0",
- "proc-macro-hack",
 ]
 
 [[package]]
@@ -7436,20 +7075,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "phf_macros"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
- "proc-macro-hack",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "phf_macros"
 version = "0.11.2"
@@ -7492,18 +7117,18 @@ dependencies = [
 
 [[package]]
 name = "pin-project"
-version = "1.1.3"
+version = "1.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0"
 dependencies = [
  "pin-project-internal",
 ]
 
 [[package]]
 name = "pin-project-internal"
-version = "1.1.3"
+version = "1.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -7556,9 +7181,9 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.28"
+version = "0.3.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
+checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
 
 [[package]]
 name = "plasmo"
@@ -7582,12 +7207,6 @@ dependencies = [
  "tokio",
 ]
 
-[[package]]
-name = "platforms"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
-
 [[package]]
 name = "plist"
 version = "1.6.0"
@@ -7595,7 +7214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
 dependencies = [
  "base64 0.21.7",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "line-wrap",
  "quick-xml",
  "serde",
@@ -7632,9 +7251,9 @@ dependencies = [
 
 [[package]]
 name = "png"
-version = "0.17.10"
+version = "0.17.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64"
+checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a"
 dependencies = [
  "bitflags 1.3.2",
  "crc32fast",
@@ -7661,14 +7280,14 @@ dependencies = [
 
 [[package]]
 name = "polling"
-version = "3.3.1"
+version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e"
+checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14"
 dependencies = [
  "cfg-if",
  "concurrent-queue",
  "pin-project-lite",
- "rustix 0.38.28",
+ "rustix 0.38.31",
  "tracing",
  "windows-sys 0.52.0",
 ]
@@ -7749,15 +7368,6 @@ dependencies = [
  "syn 2.0.48",
 ]
 
-[[package]]
-name = "primeorder"
-version = "0.13.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
-dependencies = [
- "elliptic-curve",
-]
-
 [[package]]
 name = "proc-macro-crate"
 version = "1.3.1"
@@ -7774,7 +7384,16 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8"
 dependencies = [
- "toml_edit 0.20.2",
+ "toml_edit 0.20.7",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
+dependencies = [
+ "toml_edit 0.21.1",
 ]
 
 [[package]]
@@ -7809,9 +7428,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.76"
+version = "1.0.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
 dependencies = [
  "unicode-ident",
 ]
@@ -7837,18 +7456,18 @@ checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf"
 
 [[package]]
 name = "profiling"
-version = "1.0.13"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d135ede8821cf6376eb7a64148901e1690b788c11ae94dc297ae917dbc91dc0e"
+checksum = "0f0f7f43585c34e4fdd7497d746bc32e14458cf11c69341cc0587b1d825dde42"
 dependencies = [
  "profiling-procmacros",
 ]
 
 [[package]]
 name = "profiling-procmacros"
-version = "1.0.13"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b322d7d65c1ab449be3c890fcbd0db6e1092d0dd05d79dba2dd28032cebeb05"
+checksum = "ce97fecd27bc49296e5e20518b5a1bb54a14f7d5fe6228bc9686ee2a74915cc8"
 dependencies = [
  "quote",
  "syn 2.0.48",
@@ -7883,16 +7502,6 @@ dependencies = [
  "bytemuck",
 ]
 
-[[package]]
-name = "query_segments_demo"
-version = "0.1.0"
-dependencies = [
- "dioxus",
- "dioxus-router",
- "dioxus-web",
- "form_urlencoded",
-]
-
 [[package]]
 name = "quick-error"
 version = "1.2.3"
@@ -8026,27 +7635,18 @@ dependencies = [
  "rand_core 0.5.1",
 ]
 
-[[package]]
-name = "rand_xoshiro"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
-dependencies = [
- "rand_core 0.6.4",
-]
-
 [[package]]
 name = "ratatui"
 version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "cassowary",
  "crossterm 0.27.0",
  "indoc",
  "itertools 0.11.0",
- "lru 0.12.1",
+ "lru 0.12.2",
  "paste",
  "strum",
  "unicode-segmentation",
@@ -8106,7 +7706,7 @@ dependencies = [
  "cc",
  "cfg-if",
  "interpolate_name",
- "itertools 0.12.0",
+ "itertools 0.12.1",
  "libc",
  "libfuzzer-sys 0.4.7",
  "log",
@@ -8114,7 +7714,7 @@ dependencies = [
  "nasm-rs",
  "new_debug_unreachable",
  "noop_proc_macro",
- "num-derive 0.4.1",
+ "num-derive 0.4.2",
  "num-traits",
  "once_cell",
  "paste",
@@ -8157,9 +7757,9 @@ checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544"
 
 [[package]]
 name = "rayon"
-version = "1.8.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
+checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
 dependencies = [
  "either",
  "rayon-core",
@@ -8167,9 +7767,9 @@ dependencies = [
 
 [[package]]
 name = "rayon-core"
-version = "1.12.0"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
 dependencies = [
  "crossbeam-deque",
  "crossbeam-utils",
@@ -8217,13 +7817,13 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.2"
+version = "1.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
 dependencies = [
  "aho-corasick 1.1.2",
  "memchr",
- "regex-automata 0.4.3",
+ "regex-automata 0.4.5",
  "regex-syntax 0.8.2",
 ]
 
@@ -8238,9 +7838,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
 dependencies = [
  "aho-corasick 1.1.2",
  "memchr",
@@ -8277,18 +7877,18 @@ dependencies = [
 
 [[package]]
 name = "rend"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd"
+checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
 dependencies = [
  "bytecheck",
 ]
 
 [[package]]
 name = "reqwest"
-version = "0.11.23"
+version = "0.11.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
+checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
 dependencies = [
  "base64 0.21.7",
  "bytes",
@@ -8314,6 +7914,7 @@ dependencies = [
  "serde",
  "serde_json",
  "serde_urlencoded",
+ "sync_wrapper",
  "system-configuration",
  "tokio",
  "tokio-native-tls",
@@ -8340,16 +7941,6 @@ dependencies = [
  "quick-error 1.2.3",
 ]
 
-[[package]]
-name = "rfc6979"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
-dependencies = [
- "hmac",
- "subtle",
-]
-
 [[package]]
 name = "rfd"
 version = "0.12.1"
@@ -8389,7 +7980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3625f343d89990133d013e39c46e350915178cf94f1bec9f49b0cbef98a3e3c"
 dependencies = [
  "ahash 0.8.7",
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "instant",
  "num-traits",
  "once_cell",
@@ -8440,9 +8031,9 @@ dependencies = [
 
 [[package]]
 name = "rkyv"
-version = "0.7.43"
+version = "0.7.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5"
+checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
 dependencies = [
  "bitvec",
  "bytecheck",
@@ -8458,9 +8049,9 @@ dependencies = [
 
 [[package]]
 name = "rkyv_derive"
-version = "0.7.43"
+version = "0.7.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033"
+checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -8487,7 +8078,7 @@ dependencies = [
  "either",
  "figment",
  "futures",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "log",
  "memchr",
  "multer 2.1.0",
@@ -8518,7 +8109,7 @@ checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c"
 dependencies = [
  "devise",
  "glob",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "proc-macro2",
  "quote",
  "rocket_http",
@@ -8538,7 +8129,7 @@ dependencies = [
  "futures",
  "http 0.2.11",
  "hyper 0.14.28",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "log",
  "memchr",
  "pear",
@@ -8571,8 +8162,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
 dependencies = [
  "base64 0.21.7",
- "bitflags 2.4.1",
- "indexmap 2.1.0",
+ "bitflags 2.4.2",
+ "indexmap 2.2.2",
  "serde",
  "serde_derive",
 ]
@@ -8648,9 +8239,9 @@ dependencies = [
 
 [[package]]
 name = "rust_decimal"
-version = "1.33.1"
+version = "1.34.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4"
+checksum = "755392e1a2f77afd95580d3f0d0e94ac83eeeb7167552c9b5bca549e61a94d83"
 dependencies = [
  "arrayvec",
  "borsh",
@@ -8721,14 +8312,14 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.28"
+version = "0.38.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
+checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "errno",
  "libc",
- "linux-raw-sys 0.4.12",
+ "linux-raw-sys 0.4.13",
  "windows-sys 0.52.0",
 ]
 
@@ -8827,7 +8418,7 @@ dependencies = [
  "bytes",
  "flate2",
  "futures-util",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "salvo_core",
  "tokio",
  "tokio-util",
@@ -8892,7 +8483,7 @@ dependencies = [
  "http-body-util",
  "hyper 1.1.0",
  "hyper-util",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "mime",
  "mime-infer",
  "multer 3.0.0",
@@ -8990,23 +8581,6 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
-[[package]]
-name = "scraper"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59e25654b5e9fd557a67dbaab5a5d36b8c448d0561beb4c041b6dbb902eddfa6"
-dependencies = [
- "ahash 0.8.7",
- "cssparser 0.29.6",
- "ego-tree",
- "getopts",
- "html5ever",
- "once_cell",
- "selectors 0.24.0",
- "smallvec",
- "tendril",
-]
-
 [[package]]
 name = "sct"
 version = "0.7.1"
@@ -9023,20 +8597,6 @@ version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
 
-[[package]]
-name = "sec1"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
-dependencies = [
- "base16ct",
- "der",
- "generic-array 0.14.7",
- "pkcs8",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "security-framework"
 version = "2.9.2"
@@ -9075,29 +8635,11 @@ dependencies = [
  "phf 0.8.0",
  "phf_codegen 0.8.0",
  "precomputed-hash",
- "servo_arc 0.1.1",
+ "servo_arc",
  "smallvec",
  "thin-slice",
 ]
 
-[[package]]
-name = "selectors"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416"
-dependencies = [
- "bitflags 1.3.2",
- "cssparser 0.29.6",
- "derive_more",
- "fxhash",
- "log",
- "phf 0.8.0",
- "phf_codegen 0.8.0",
- "precomputed-hash",
- "servo_arc 0.2.0",
- "smallvec",
-]
-
 [[package]]
 name = "semver"
 version = "1.0.21"
@@ -9115,9 +8657,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5"
 
 [[package]]
 name = "serde"
-version = "1.0.195"
+version = "1.0.196"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
+checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
 dependencies = [
  "serde_derive",
 ]
@@ -9167,9 +8709,9 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.195"
+version = "1.0.196"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
+checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -9178,9 +8720,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.111"
+version = "1.0.113"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
+checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
 dependencies = [
  "itoa 1.0.10",
  "ryu",
@@ -9197,15 +8739,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "serde_plain"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50"
-dependencies = [
- "serde",
-]
-
 [[package]]
 name = "serde_qs"
 version = "0.12.0"
@@ -9261,15 +8794,15 @@ dependencies = [
 
 [[package]]
 name = "serde_with"
-version = "3.4.0"
+version = "3.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
+checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981"
 dependencies = [
  "base64 0.21.7",
  "chrono",
  "hex",
  "indexmap 1.9.3",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "serde",
  "serde_json",
  "serde_with_macros",
@@ -9278,9 +8811,9 @@ dependencies = [
 
 [[package]]
 name = "serde_with_macros"
-version = "3.4.0"
+version = "3.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
+checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15"
 dependencies = [
  "darling",
  "proc-macro2",
@@ -9290,9 +8823,9 @@ dependencies = [
 
 [[package]]
 name = "server_fn"
-version = "0.5.4"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfed18dfcc8d9004579c40482c3419c07f60ffb9c5b250542edca99f508b0ce9"
+checksum = "6c265de965fe48e09ad8899d0ab1ffebdfa1a9914e4de5ff107b07bd94cf7541"
 dependencies = [
  "ciborium",
  "const_format",
@@ -9315,9 +8848,9 @@ dependencies = [
 
 [[package]]
 name = "server_fn_macro"
-version = "0.5.4"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b70ae8e22546ba85500391b36c08e3fba64871be8a26557a3663a8e08acb56f"
+checksum = "f77000541a62ceeec01eef3ee0f86c155c33dac5fae750ad04a40852c6d5469a"
 dependencies = [
  "const_format",
  "proc-macro-error",
@@ -9330,9 +8863,9 @@ dependencies = [
 
 [[package]]
 name = "server_fn_macro_default"
-version = "0.5.4"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7256ba61dfadb220598db418376e7bc2a34b96df36c4dc48f24ffe161810fc0b"
+checksum = "8a3353f22e2bcc451074d4feaa37317d9d17dff11d4311928384734ea17ab9ca"
 dependencies = [
  "server_fn_macro",
  "syn 2.0.48",
@@ -9348,16 +8881,6 @@ dependencies = [
  "stable_deref_trait",
 ]
 
-[[package]]
-name = "servo_arc"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741"
-dependencies = [
- "nodrop",
- "stable_deref_trait",
-]
-
 [[package]]
 name = "sha-1"
 version = "0.10.1"
@@ -9523,16 +9046,6 @@ version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
 
-[[package]]
-name = "sized-chunks"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
-dependencies = [
- "bitmaps",
- "typenum",
-]
-
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -9572,17 +9085,11 @@ dependencies = [
  "version_check",
 ]
 
-[[package]]
-name = "smallbox"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d92359f97e6b417da4328a970cf04a044db104fbd57f7d72cb7ff665bb8806af"
-
 [[package]]
 name = "smallvec"
-version = "1.11.2"
+version = "1.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
 
 [[package]]
 name = "smartstring"
@@ -9681,7 +9188,7 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c"
 dependencies = [
- "itertools 0.12.0",
+ "itertools 0.12.1",
  "nom",
  "unicode_categories",
 ]
@@ -9724,7 +9231,7 @@ dependencies = [
  "futures-util",
  "hashlink",
  "hex",
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "ipnetwork",
  "log",
  "mac_address",
@@ -9799,7 +9306,7 @@ dependencies = [
  "atoi",
  "base64 0.21.7",
  "bigdecimal",
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "byteorder",
  "bytes",
  "chrono",
@@ -9847,7 +9354,7 @@ dependencies = [
  "base64 0.21.7",
  "bigdecimal",
  "bit-vec",
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "byteorder",
  "chrono",
  "crc",
@@ -9955,9 +9462,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
 [[package]]
 name = "str-buf"
-version = "3.0.2"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e75b72ee54e2f93c3ea1354066162be893ee5e25773ab743de3e088cecbb4f31"
+checksum = "0ceb97b7225c713c2fd4db0153cb6b3cab244eb37900c3f634ed4d43310d8c34"
 
 [[package]]
 name = "string_cache"
@@ -10120,7 +9627,7 @@ dependencies = [
  "cfg-expr 0.15.6",
  "heck 0.4.1",
  "pkg-config",
- "toml 0.8.8",
+ "toml 0.8.10",
  "version-compare",
 ]
 
@@ -10138,9 +9645,9 @@ dependencies = [
 
 [[package]]
 name = "tao"
-version = "0.24.0"
+version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c0dff18fed076d29cb5779e918ef4b8a5dbb756204e4a027794f0bce233d949"
+checksum = "75d4a64cfac8e487c61d778fe4ab8480f162451e8af7f247aafadcb3b2560852"
 dependencies = [
  "bitflags 1.3.2",
  "cc",
@@ -10300,7 +9807,7 @@ dependencies = [
  "cfg-if",
  "fastrand 2.0.1",
  "redox_syscall",
- "rustix 0.38.28",
+ "rustix 0.38.31",
  "windows-sys 0.48.0",
 ]
 
@@ -10381,9 +9888,9 @@ dependencies = [
 
 [[package]]
 name = "tiff"
-version = "0.9.0"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
 dependencies = [
  "flate2",
  "jpeg-decoder",
@@ -10392,13 +9899,14 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.31"
+version = "0.3.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
+checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
 dependencies = [
  "deranged",
  "itoa 1.0.10",
  "libc",
+ "num-conv",
  "num_threads",
  "powerfmt",
  "serde",
@@ -10414,10 +9922,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.16"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
+checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
 dependencies = [
+ "num-conv",
  "time-core",
 ]
 
@@ -10463,9 +9972,9 @@ checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8"
 
 [[package]]
 name = "tokio"
-version = "1.35.1"
+version = "1.36.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
+checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
 dependencies = [
  "backtrace",
  "bytes",
@@ -10609,15 +10118,15 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.8.8"
+version = "0.8.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
+checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
 dependencies = [
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "serde",
  "serde_spanned",
  "toml_datetime",
- "toml_edit 0.21.0",
+ "toml_edit 0.22.4",
 ]
 
 [[package]]
@@ -10635,7 +10144,7 @@ version = "0.19.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
 dependencies = [
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -10644,22 +10153,33 @@ dependencies = [
 
 [[package]]
 name = "toml_edit"
-version = "0.20.2"
+version = "0.20.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
+checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
 dependencies = [
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "toml_datetime",
  "winnow",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.21.0"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
+dependencies = [
+ "indexmap 2.2.2",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
+checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951"
 dependencies = [
- "indexmap 2.1.0",
+ "indexmap 2.2.2",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -10736,9 +10256,9 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
 dependencies = [
- "async-compression 0.4.5",
+ "async-compression 0.4.6",
  "base64 0.21.7",
- "bitflags 2.4.1",
+ "bitflags 2.4.2",
  "bytes",
  "futures-core",
  "futures-util",
@@ -10854,9 +10374,9 @@ dependencies = [
 
 [[package]]
 name = "treediff"
-version = "4.0.2"
+version = "4.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303"
+checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5"
 dependencies = [
  "serde_json",
 ]
@@ -10915,9 +10435,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
 [[package]]
 name = "trybuild"
-version = "1.0.88"
+version = "1.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76de4f783e610194f6c98bfd53f9fc52bb2e0d02c947621e8a0f4ecc799b2880"
+checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f"
 dependencies = [
  "basic-toml",
  "dissimilar",
@@ -11015,18 +10535,20 @@ dependencies = [
 
 [[package]]
 name = "ulid"
-version = "1.1.0"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e37c4b6cbcc59a8dcd09a6429fbc7890286bcbb79215cea7b38a3c4c0921d93"
+checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259"
 dependencies = [
+ "getrandom 0.2.12",
  "rand 0.8.5",
+ "web-time",
 ]
 
 [[package]]
 name = "uncased"
-version = "0.9.9"
+version = "0.9.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68"
+checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
 dependencies = [
  "serde",
  "version_check",
@@ -11043,9 +10565,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.14"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
 
 [[package]]
 name = "unicode-bom"
@@ -11070,9 +10592,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.10.1"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
 
 [[package]]
 name = "unicode-width"
@@ -11162,9 +10684,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
 name = "uuid"
-version = "1.6.1"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
+checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
 dependencies = [
  "getrandom 0.2.12",
  "serde",
@@ -11179,7 +10701,7 @@ checksum = "c372e4e6fad129795fb86fda6021b258948560b39883b80ed00510a7d19846b0"
 dependencies = [
  "cfg-if",
  "noop_proc_macro",
- "num-derive 0.4.1",
+ "num-derive 0.4.2",
  "num-traits",
  "profiling",
 ]
@@ -11192,9 +10714,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
 [[package]]
 name = "value-bag"
-version = "1.6.0"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503"
+checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b"
 
 [[package]]
 name = "vcpkg"
@@ -11310,16 +10832,11 @@ name = "warp-hello-world"
 version = "0.1.0"
 dependencies = [
  "dioxus",
- "dioxus-fullstack",
- "dioxus-web",
- "execute",
  "reqwest",
  "serde",
- "simple_logger",
  "tracing",
  "tracing-subscriber",
  "tracing-wasm",
- "warp",
 ]
 
 [[package]]
@@ -11336,9 +10853,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -11346,9 +10863,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
 dependencies = [
  "bumpalo",
  "log",
@@ -11361,12 +10878,12 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-cli-support"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf8226e223e2dfbe8f921b7f20b82d1b5d86a6b143e9d6286cca8edd16695583"
+checksum = "806a045c4ec4ef7c3ad86dc27bcb641b84d9eeb3846200f56d7ab0885241d654"
 dependencies = [
  "anyhow",
- "base64 0.9.3",
+ "base64 0.21.7",
  "log",
  "rustc-demangle",
  "serde_json",
@@ -11383,9 +10900,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-externref-xform"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a719be856d8b0802c7195ca26ee6eb02cb9639a12b80be32db960ce9640cb8"
+checksum = "12b6ac5fca1d0992d2328147488169ea166bfe899c88f8ad06cf583f4c492fcf"
 dependencies = [
  "anyhow",
  "walrus",
@@ -11393,9 +10910,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.39"
+version = "0.4.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
+checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -11405,9 +10922,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -11415,9 +10932,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -11428,9 +10945,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-multi-value-xform"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12766255d4b9026700376cc81894eeb62903e4414cbc94675f6f9babd9cfb76"
+checksum = "d1e019acde479e2f090fb7f14a51fa0077ec3a7bb12a56e0e888a82be7b5bd3f"
 dependencies = [
  "anyhow",
  "walrus",
@@ -11438,15 +10955,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
 
 [[package]]
 name = "wasm-bindgen-test"
-version = "0.3.39"
+version = "0.3.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403"
+checksum = "143ddeb4f833e2ed0d252e618986e18bfc7b0e52f2d28d77d05b2f045dd8eb61"
 dependencies = [
  "console_error_panic_hook",
  "js-sys",
@@ -11458,9 +10975,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-test-macro"
-version = "0.3.39"
+version = "0.3.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89"
+checksum = "a5211b7550606857312bba1d978a8ec75692eae187becc5e680444fffc5e6f89"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -11469,9 +10986,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-threads-xform"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13c2b14c5b9c2c7aa9dd1eb7161857de9783f40e98582e7f41f2d7c04ffdc155"
+checksum = "90a2e577034352f9aa9352730fcf2562c68957f2e9b9ee70ab6379510e49e2fe"
 dependencies = [
  "anyhow",
  "walrus",
@@ -11480,9 +10997,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-wasm-conventions"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaedf88769cb23c6fd2e3bfed65bcbff6c5d92c8336afbd80d2dfcc8eb5cf047"
+checksum = "4e6b653f6820409609bda0f176e6949302307af7a7b9479cd4d4b1bdc31eb9cd"
 dependencies = [
  "anyhow",
  "walrus",
@@ -11490,9 +11007,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-wasm-interpreter"
-version = "0.2.89"
+version = "0.2.91"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8a79039df1e0822e6d66508ec86052993deac201e26060f62abcd85e1daf951"
+checksum = "682940195a701dbf887f20017418b8cac916a37b3f91ededec33226619e973c1"
 dependencies = [
  "anyhow",
  "log",
@@ -11522,9 +11039,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-streams"
-version = "0.3.0"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
+checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
 dependencies = [
  "futures-util",
  "js-sys",
@@ -11541,9 +11058,19 @@ checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b"
 
 [[package]]
 name = "web-sys"
-version = "0.3.66"
+version = "0.3.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
+checksum = "2ee269d72cc29bf77a2c4bc689cc750fb39f5cbd493d2205bbb3f5c7779cf7b0"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -11631,9 +11158,9 @@ dependencies = [
 
 [[package]]
 name = "webpki-roots"
-version = "0.25.3"
+version = "0.25.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
+checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
 
 [[package]]
 name = "webview2-com"
@@ -11673,9 +11200,9 @@ dependencies = [
 
 [[package]]
 name = "weezl"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
 
 [[package]]
 name = "which"
@@ -11686,7 +11213,7 @@ dependencies = [
  "either",
  "home",
  "once_cell",
- "rustix 0.38.28",
+ "rustix 0.38.31",
 ]
 
 [[package]]
@@ -11993,9 +11520,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
 
 [[package]]
 name = "winnow"
-version = "0.5.34"
+version = "0.5.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
+checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29"
 dependencies = [
  "memchr",
 ]
@@ -12087,22 +11614,22 @@ dependencies = [
 
 [[package]]
 name = "xattr"
-version = "1.2.0"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1"
+checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
 dependencies = [
  "libc",
- "linux-raw-sys 0.4.12",
- "rustix 0.38.28",
+ "linux-raw-sys 0.4.13",
+ "rustix 0.38.31",
 ]
 
 [[package]]
 name = "xdg-home"
-version = "1.0.0"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd"
+checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e"
 dependencies = [
- "nix 0.26.4",
+ "libc",
  "winapi",
 ]
 
@@ -12135,9 +11662,9 @@ dependencies = [
 
 [[package]]
 name = "zbus"
-version = "3.14.1"
+version = "3.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948"
+checksum = "c45d06ae3b0f9ba1fb2671268b975557d8f5a84bb5ec6e43964f87e763d8bca8"
 dependencies = [
  "async-broadcast",
  "async-executor",
@@ -12176,9 +11703,9 @@ dependencies = [
 
 [[package]]
 name = "zbus_macros"
-version = "3.14.1"
+version = "3.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d"
+checksum = "b4a1ba45ed0ad344b85a2bb5a1fe9830aed23d67812ea39a586e7d0136439c7d"
 dependencies = [
  "proc-macro-crate 1.3.1",
  "proc-macro2",

+ 65 - 38
Cargo.toml

@@ -2,10 +2,12 @@
 resolver = "2"
 members = [
     "packages/dioxus",
+    "packages/dioxus-lib",
     "packages/core",
     "packages/cli",
     "packages/cli-config",
     "packages/core-macro",
+    "packages/config-macro",
     "packages/router-macro",
     "packages/extension",
     "packages/router",
@@ -17,13 +19,12 @@ members = [
     "packages/desktop",
     "packages/mobile",
     "packages/interpreter",
-    "packages/fermi",
     "packages/liveview",
     "packages/autofmt",
     "packages/check",
     "packages/rsx",
     "packages/dioxus-tui",
-    "packages/rink",
+    "packages/plasmo",
     "packages/native-core",
     "packages/native-core-macro",
     "packages/rsx-rosetta",
@@ -42,14 +43,13 @@ members = [
     # Full project examples
     "examples/tailwind",
     "examples/PWA-example",
-    "examples/query_segments_demo",
-    "examples/openid_connect_demo",
+    # "examples/openid_connect_demo",
     # Playwright tests
-    "playwright-tests/liveview",
-    "playwright-tests/web",
-    "playwright-tests/fullstack",
+    "packages/playwright-tests/liveview",
+    "packages/playwright-tests/web",
+    "packages/playwright-tests/fullstack",
 ]
-exclude = ["examples/mobile_demo"]
+exclude = ["examples/mobile_demo", "examples/openid_connect_demo",]
 
 [workspace.package]
 version = "0.4.3"
@@ -57,25 +57,26 @@ version = "0.4.3"
 # dependencies that are shared across packages
 [workspace.dependencies]
 dioxus = { path = "packages/dioxus", version = "0.4.0" }
+dioxus-lib = { path = "packages/dioxus-lib", version = "0.4.0" }
 dioxus-core = { path = "packages/core", version = "0.4.2" }
 dioxus-core-macro = { path = "packages/core-macro", version = "0.4.0" }
-dioxus-router = { path = "packages/router", version = "0.4.1" }
+dioxus-config-macro = { path = "packages/config-macro", version = "0.4.0" }
+dioxus-router = { path = "packages/router", version = "0.4.1"  }
 dioxus-router-macro = { path = "packages/router-macro", version = "0.4.1" }
-dioxus-html = { path = "packages/html", default-features = false, version = "0.4.0" }
-dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.4.0" }
+dioxus-html = { path = "packages/html", version = "0.4.0"  }
+dioxus-html-internal-macro = { path = "packages/html-internal-macro", version = "0.4.0"  }
 dioxus-hooks = { path = "packages/hooks", version = "0.4.0" }
 dioxus-web = { path = "packages/web", version = "0.4.0" }
-dioxus-ssr = { path = "packages/ssr", version = "0.4.0" }
+dioxus-ssr = { path = "packages/ssr", version = "0.4.0", default-features = false }
 dioxus-desktop = { path = "packages/desktop", version = "0.4.0" }
-dioxus-mobile = { path = "packages/mobile", version = "0.4.0" }
+dioxus-mobile = { path = "packages/mobile", version = "0.4.0"  }
 dioxus-interpreter-js = { path = "packages/interpreter", version = "0.4.0" }
-fermi = { path = "packages/fermi", version = "0.4.0" }
-dioxus-liveview = { path = "packages/liveview", version = "0.4.0" }
-dioxus-autofmt = { path = "packages/autofmt", version = "0.4.0" }
-dioxus-check = { path = "packages/check", version = "0.4.0" }
-dioxus-rsx = { path = "packages/rsx", version = "0.4.0" }
-dioxus-tui = { path = "packages/dioxus-tui", version = "0.4.0" }
-plasmo = { path = "packages/rink", version = "0.4.0" }
+dioxus-liveview = { path = "packages/liveview", version = "0.4.0"  }
+dioxus-autofmt = { path = "packages/autofmt", version = "0.4.0"  }
+dioxus-check = { path = "packages/check", version = "0.4.0"  }
+dioxus-rsx = { path = "packages/rsx", version = "0.4.0"  }
+dioxus-tui = { path = "packages/dioxus-tui", version = "0.4.0"  }
+plasmo = { path = "packages/plasmo", version = "0.4.0" }
 dioxus-native-core = { path = "packages/native-core", version = "0.4.0" }
 dioxus-native-core-macro = { path = "packages/native-core-macro", version = "0.4.0" }
 rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.4.0" }
@@ -98,11 +99,11 @@ thiserror = "1.0.40"
 prettyplease = { package = "prettier-please", version = "0.2", features = [
     "verbatim",
 ] }
-manganis-cli-support = { git = "https://github.com/DioxusLabs/collect-assets", rev = "e0093a4", features = [
+manganis-cli-support = { git = "https://github.com/DioxusLabs/collect-assets", rev = "f982698", features = [
     "webp",
     "html",
 ] }
-manganis = { git = "https://github.com/DioxusLabs/collect-assets", rev = "e0093a4" }
+manganis = { git = "https://github.com/DioxusLabs/collect-assets", rev = "f982698" }
 
 # This is a "virtual package"
 # It is not meant to be published, but is used so "cargo run --example XYZ" works properly
@@ -120,27 +121,53 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 rust-version = "1.60.0"
 publish = false
 
+[dependencies]
+manganis = { workspace = true, optional = true}
+reqwest = { version = "0.11.9", features = ["json"], optional = true}
+http-range = {version = "0.1.5", optional = true }
+
 [dev-dependencies]
-dioxus = { workspace = true }
-dioxus-desktop = { workspace = true, features = ["transparent"] }
+dioxus = { workspace = true, features = ["router"] }
 dioxus-ssr = { workspace = true }
-dioxus-router = { workspace = true }
-dioxus-signals = { workspace = true }
-fermi = { workspace = true }
 futures-util = "0.3.21"
-log = "0.4.14"
-num-format = "0.4.0"
 separator = "0.4.1"
 serde = { version = "1.0.136", features = ["derive"] }
-im-rc = "15.0.0"
-anyhow = "1.0.53"
 serde_json = "1.0.79"
 rand = { version = "0.8.4", features = ["small_rng"] }
+form_urlencoded = "1.2.0"
+
+[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
+getrandom = { version = "0.2.12", features = ["js"] }
+
+[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
 tokio = { version = "1.16.1", features = ["full"] }
-reqwest = { version = "0.11.9", features = ["json"] }
-env_logger = "0.10.0"
-simple_logger = "4.0.0"
-thiserror = { workspace = true }
-manganis = { workspace = true }
-tracing-subscriber = "0.3.17"
-http-range = "0.1.5"
+
+# To make most examples faster to compile, we split out assets and http-related stuff
+# This trims off like 270 dependencies, leading to a significant speedup in compilation time
+[features]
+liveview = ["dioxus/liveview"]
+fullstack = ["dioxus/fullstack"]
+axum = ["dioxus/axum"]
+salvo = ["dioxus/salvo"]
+rocket = ["dioxus/rocket"]
+server = ["dioxus/axum"]
+default = ["dioxus/desktop"]
+web = ["dioxus/web"]
+collect-assets = ["manganis"]
+http = ["reqwest", "http-range"]
+
+[[example]]
+name = "login_form"
+required-features = ["http"]
+
+[[example]]
+name = "dog_app"
+required-features = ["http"]
+
+[[example]]
+name = "video_stream"
+required-features = ["http"]
+
+[[example]]
+name = "suspense"
+required-features = ["http"]

+ 7 - 4
README.md

@@ -52,17 +52,20 @@
 
 <br/>
 
+> [!WARNING]
+> Dioxus 0.5 (currently in master) contains massive breaking changes and is not compatible with Dioxus 0.4
+
 Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust.
 
 ```rust
-fn app(cx: Scope) -> Element {
-    let mut count = use_state(cx, || 0);
+fn app() -> Element {
+    let mut count = use_signal(|| 0);
 
-    cx.render(rsx! {
+    rsx! {
         h1 { "High-Five counter: {count}" }
         button { onclick: move |_| count += 1, "Up high!" }
         button { onclick: move |_| count -= 1, "Down low!" }
-    })
+    }
 }
 ```
 

+ 8 - 7
examples/PWA-example/src/main.rs

@@ -5,16 +5,17 @@ fn main() {
     wasm_logger::init(wasm_logger::Config::default());
     console_error_panic_hook::set_once();
 
-    dioxus_web::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! (
-        div {
-            style: "text-align: center;",
+fn app() -> Element {
+    rsx! (
+        div { style: "text-align: center;",
             h1 { "🌗 Dioxus 🚀" }
             h3 { "Frontend that scales." }
-            p { "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust." }
+            p {
+                "Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust."
+            }
         }
-    ))
+    )
 }

+ 0 - 413
examples/all_css.rs

@@ -1,413 +0,0 @@
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
-        div {
-            align_content: "a",
-            align_items: "a",
-            align_self: "a",
-            alignment_adjust: "a",
-            alignment_baseline: "a",
-            all: "a",
-            alt: "a",
-            animation: "a",
-            animation_delay: "a",
-            animation_direction: "a",
-            animation_duration: "a",
-            animation_fill_mode: "a",
-            animation_iteration_count: "a",
-            animation_name: "a",
-            animation_play_state: "a",
-            animation_timing_function: "a",
-            azimuth: "a",
-            backface_visibility: "a",
-            background: "a",
-            background_attachment: "a",
-            background_clip: "a",
-            background_color: "a",
-            background_image: "a",
-            background_origin: "a",
-            background_position: "a",
-            background_repeat: "a",
-            background_size: "a",
-            background_blend_mode: "a",
-            baseline_shift: "a",
-            bleed: "a",
-            bookmark_label: "a",
-            bookmark_level: "a",
-            bookmark_state: "a",
-            border: "a",
-            border_color: "a",
-            border_style: "a",
-            border_width: "a",
-            border_bottom: "a",
-            border_bottom_color: "a",
-            border_bottom_style: "a",
-            border_bottom_width: "a",
-            border_left: "a",
-            border_left_color: "a",
-            border_left_style: "a",
-            border_left_width: "a",
-            border_right: "a",
-            border_right_color: "a",
-            border_right_style: "a",
-            border_right_width: "a",
-            border_top: "a",
-            border_top_color: "a",
-            border_top_style: "a",
-            border_top_width: "a",
-            border_collapse: "a",
-            border_image: "a",
-            border_image_outset: "a",
-            border_image_repeat: "a",
-            border_image_slice: "a",
-            border_image_source: "a",
-            border_image_width: "a",
-            border_radius: "a",
-            border_bottom_left_radius: "a",
-            border_bottom_right_radius: "a",
-            border_top_left_radius: "a",
-            border_top_right_radius: "a",
-            border_spacing: "a",
-            bottom: "a",
-            box_decoration_break: "a",
-            box_shadow: "a",
-            box_sizing: "a",
-            box_snap: "a",
-            break_after: "a",
-            break_before: "a",
-            break_inside: "a",
-            buffered_rendering: "a",
-            caption_side: "a",
-            clear: "a",
-            clear_side: "a",
-            clip: "a",
-            clip_path: "a",
-            clip_rule: "a",
-            color: "a",
-            color_adjust: "a",
-            color_correction: "a",
-            color_interpolation: "a",
-            color_interpolation_filters: "a",
-            color_profile: "a",
-            color_rendering: "a",
-            column_fill: "a",
-            column_gap: "a",
-            column_rule: "a",
-            column_rule_color: "a",
-            column_rule_style: "a",
-            column_rule_width: "a",
-            column_span: "a",
-            columns: "a",
-            column_count: "a",
-            column_width: "a",
-            contain: "a",
-            content: "a",
-            counter_increment: "a",
-            counter_reset: "a",
-            counter_set: "a",
-            cue: "a",
-            cue_after: "a",
-            cue_before: "a",
-            cursor: "a",
-            direction: "a",
-            display: "a",
-            display_inside: "a",
-            display_outside: "a",
-            display_extras: "a",
-            display_box: "a",
-            dominant_baseline: "a",
-            elevation: "a",
-            empty_cells: "a",
-            enable_background: "a",
-            fill: "a",
-            fill_opacity: "a",
-            fill_rule: "a",
-            filter: "a",
-            float: "a",
-            float_defer_column: "a",
-            float_defer_page: "a",
-            float_offset: "a",
-            float_wrap: "a",
-            flow_into: "a",
-            flow_from: "a",
-            flex: "a",
-            flex_basis: "a",
-            flex_grow: "a",
-            flex_shrink: "a",
-            flex_flow: "a",
-            flex_direction: "a",
-            flex_wrap: "a",
-            flood_color: "a",
-            flood_opacity: "a",
-            font: "a",
-            font_family: "a",
-            font_size: "a",
-            font_stretch: "a",
-            font_style: "a",
-            font_weight: "a",
-            font_feature_settings: "a",
-            font_kerning: "a",
-            font_language_override: "a",
-            font_size_adjust: "a",
-            font_synthesis: "a",
-            font_variant: "a",
-            font_variant_alternates: "a",
-            font_variant_caps: "a",
-            font_variant_east_asian: "a",
-            font_variant_ligatures: "a",
-            font_variant_numeric: "a",
-            font_variant_position: "a",
-            footnote_policy: "a",
-            glyph_orientation_horizontal: "a",
-            glyph_orientation_vertical: "a",
-            grid: "a",
-            grid_auto_flow: "a",
-            grid_auto_columns: "a",
-            grid_auto_rows: "a",
-            grid_template: "a",
-            grid_template_areas: "a",
-            grid_template_columns: "a",
-            grid_template_rows: "a",
-            grid_area: "a",
-            grid_column: "a",
-            grid_column_start: "a",
-            grid_column_end: "a",
-            grid_row: "a",
-            grid_row_start: "a",
-            grid_row_end: "a",
-            hanging_punctuation: "a",
-            height: "a",
-            hyphenate_character: "a",
-            hyphenate_limit_chars: "a",
-            hyphenate_limit_last: "a",
-            hyphenate_limit_lines: "a",
-            hyphenate_limit_zone: "a",
-            hyphens: "a",
-            icon: "a",
-            image_orientation: "a",
-            image_resolution: "a",
-            image_rendering: "a",
-            ime: "a",
-            ime_align: "a",
-            ime_mode: "a",
-            ime_offset: "a",
-            ime_width: "a",
-            initial_letters: "a",
-            inline_box_align: "a",
-            isolation: "a",
-            justify_content: "a",
-            justify_items: "a",
-            justify_self: "a",
-            kerning: "a",
-            left: "a",
-            letter_spacing: "a",
-            lighting_color: "a",
-            line_box_contain: "a",
-            line_break: "a",
-            line_grid: "a",
-            line_height: "a",
-            line_slack: "a",
-            line_snap: "a",
-            list_style: "a",
-            list_style_image: "a",
-            list_style_position: "a",
-            list_style_type: "a",
-            margin: "a",
-            margin_bottom: "a",
-            margin_left: "a",
-            margin_right: "a",
-            margin_top: "a",
-            marker: "a",
-            marker_end: "a",
-            marker_mid: "a",
-            marker_pattern: "a",
-            marker_segment: "a",
-            marker_start: "a",
-            marker_knockout_left: "a",
-            marker_knockout_right: "a",
-            marker_side: "a",
-            marks: "a",
-            marquee_direction: "a",
-            marquee_play_count: "a",
-            marquee_speed: "a",
-            marquee_style: "a",
-            mask: "a",
-            mask_image: "a",
-            mask_repeat: "a",
-            mask_position: "a",
-            mask_clip: "a",
-            mask_origin: "a",
-            mask_size: "a",
-            mask_box: "a",
-            mask_box_outset: "a",
-            mask_box_repeat: "a",
-            mask_box_slice: "a",
-            mask_box_source: "a",
-            mask_box_width: "a",
-            mask_type: "a",
-            max_height: "a",
-            max_lines: "a",
-            max_width: "a",
-            min_height: "a",
-            min_width: "a",
-            mix_blend_mode: "a",
-            nav_down: "a",
-            nav_index: "a",
-            nav_left: "a",
-            nav_right: "a",
-            nav_up: "a",
-            object_fit: "a",
-            object_position: "a",
-            offset_after: "a",
-            offset_before: "a",
-            offset_end: "a",
-            offset_start: "a",
-            opacity: "a",
-            order: "a",
-            orphans: "a",
-            outline: "a",
-            outline_color: "a",
-            outline_style: "a",
-            outline_width: "a",
-            outline_offset: "a",
-            overflow: "a",
-            overflow_x: "a",
-            overflow_y: "a",
-            overflow_style: "a",
-            overflow_wrap: "a",
-            padding: "a",
-            padding_bottom: "a",
-            padding_left: "a",
-            padding_right: "a",
-            padding_top: "a",
-            page: "a",
-            page_break_after: "a",
-            page_break_before: "a",
-            page_break_inside: "a",
-            paint_order: "a",
-            pause: "a",
-            pause_after: "a",
-            pause_before: "a",
-            perspective: "a",
-            perspective_origin: "a",
-            pitch: "a",
-            pitch_range: "a",
-            play_during: "a",
-            pointer_events: "a",
-            position: "a",
-            quotes: "a",
-            region_fragment: "a",
-            resize: "a",
-            rest: "a",
-            rest_after: "a",
-            rest_before: "a",
-            richness: "a",
-            right: "a",
-            ruby_align: "a",
-            ruby_merge: "a",
-            ruby_position: "a",
-            scroll_behavior: "a",
-            scroll_snap_coordinate: "a",
-            scroll_snap_destination: "a",
-            scroll_snap_points_x: "a",
-            scroll_snap_points_y: "a",
-            scroll_snap_type: "a",
-            shape_image_threshold: "a",
-            shape_inside: "a",
-            shape_margin: "a",
-            shape_outside: "a",
-            shape_padding: "a",
-            shape_rendering: "a",
-            size: "a",
-            speak: "a",
-            speak_as: "a",
-            speak_header: "a",
-            speak_numeral: "a",
-            speak_punctuation: "a",
-            speech_rate: "a",
-            stop_color: "a",
-            stop_opacity: "a",
-            stress: "a",
-            string_set: "a",
-            stroke: "a",
-            stroke_dasharray: "a",
-            stroke_dashoffset: "a",
-            stroke_linecap: "a",
-            stroke_linejoin: "a",
-            stroke_miterlimit: "a",
-            stroke_opacity: "a",
-            stroke_width: "a",
-            tab_size: "a",
-            table_layout: "a",
-            text_align: "a",
-            text_align_all: "a",
-            text_align_last: "a",
-            text_anchor: "a",
-            text_combine_upright: "a",
-            text_decoration: "a",
-            text_decoration_color: "a",
-            text_decoration_line: "a",
-            text_decoration_style: "a",
-            text_decoration_skip: "a",
-            text_emphasis: "a",
-            text_emphasis_color: "a",
-            text_emphasis_style: "a",
-            text_emphasis_position: "a",
-            text_emphasis_skip: "a",
-            text_height: "a",
-            text_indent: "a",
-            text_justify: "a",
-            text_orientation: "a",
-            text_overflow: "a",
-            text_rendering: "a",
-            text_shadow: "a",
-            text_size_adjust: "a",
-            text_space_collapse: "a",
-            text_spacing: "a",
-            text_transform: "a",
-            text_underline_position: "a",
-            text_wrap: "a",
-            top: "a",
-            touch_action: "a",
-            transform: "a",
-            transform_box: "a",
-            transform_origin: "a",
-            transform_style: "a",
-            transition: "a",
-            transition_delay: "a",
-            transition_duration: "a",
-            transition_property: "a",
-            unicode_bidi: "a",
-            vector_effect: "a",
-            vertical_align: "a",
-            visibility: "a",
-            voice_balance: "a",
-            voice_duration: "a",
-            voice_family: "a",
-            voice_pitch: "a",
-            voice_range: "a",
-            voice_rate: "a",
-            voice_stress: "a",
-            voice_volumn: "a",
-            volume: "a",
-            white_space: "a",
-            widows: "a",
-            width: "a",
-            will_change: "a",
-            word_break: "a",
-            word_spacing: "a",
-            word_wrap: "a",
-            wrap_flow: "a",
-            wrap_through: "a",
-            writing_mode: "a",
-            z_index: "a",
-
-            "This example isn't quite useful yet"
-        }
-    })
-}

+ 36 - 55
examples/all_events.rs

@@ -1,78 +1,59 @@
-use dioxus::{events::*, html::MouseEvent, prelude::*};
+use dioxus::prelude::*;
+use std::{collections::VecDeque, fmt::Debug, rc::Rc};
 
 fn main() {
-    dioxus_desktop::launch(app);
-}
-
-#[derive(Debug)]
-enum Event {
-    MouseMove(MouseEvent),
-    MouseClick(MouseEvent),
-    MouseDoubleClick(MouseEvent),
-    MouseDown(MouseEvent),
-    MouseUp(MouseEvent),
-
-    Wheel(WheelEvent),
-
-    KeyDown(KeyboardEvent),
-    KeyUp(KeyboardEvent),
-    KeyPress(KeyboardEvent),
-
-    FocusIn(FocusEvent),
-    FocusOut(FocusEvent),
+    launch(app);
 }
 
 const MAX_EVENTS: usize = 8;
 
 const CONTAINER_STYLE: &str = r#"
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-    "#;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+"#;
 
 const RECT_STYLE: &str = r#"
-        background: deepskyblue;
-        height: 50vh;
-        width: 50vw;
-        color: white;
-        padding: 20px;
-        margin: 20px;
-        text-aligh: center;
-    "#;
-
-fn app(cx: Scope) -> Element {
-    let events = use_ref(cx, std::collections::VecDeque::new);
-
-    let log_event = move |event: Event| {
+    background: deepskyblue;
+    height: 50vh;
+    width: 50vw;
+    color: white;
+    padding: 20px;
+    margin: 20px;
+    text-aligh: center;
+"#;
+
+fn app() -> Element {
+    let mut events = use_signal(|| VecDeque::new() as VecDeque<Rc<dyn Debug>>);
+
+    let mut log_event = move |event: Rc<dyn Debug>| {
         let mut events = events.write();
 
         if events.len() >= MAX_EVENTS {
             events.pop_front();
         }
+
         events.push_back(event);
     };
 
-    cx.render(rsx! (
+    rsx! {
         div { style: "{CONTAINER_STYLE}",
-            div {
-                style: "{RECT_STYLE}",
-                // focusing is necessary to catch keyboard events
-                tabindex: "0",
-
-                onmousemove: move |event| log_event(Event::MouseMove(event)),
-                onclick: move |event| log_event(Event::MouseClick(event)),
-                ondoubleclick: move |event| log_event(Event::MouseDoubleClick(event)),
-                onmousedown: move |event| log_event(Event::MouseDown(event)),
-                onmouseup: move |event| log_event(Event::MouseUp(event)),
+            // focusing is necessary to catch keyboard events
+            div { style: "{RECT_STYLE}", tabindex: 0,
+                onmousemove: move |event| log_event(event.data()),
+                onclick: move |event| log_event(event.data()),
+                ondoubleclick: move |event| log_event(event.data()),
+                onmousedown: move |event| log_event(event.data()),
+                onmouseup: move |event| log_event(event.data()),
 
-                onwheel: move |event| log_event(Event::Wheel(event)),
+                onwheel: move |event| log_event(event.data()),
 
-                onkeydown: move |event| log_event(Event::KeyDown(event)),
-                onkeyup: move |event| log_event(Event::KeyUp(event)),
-                onkeypress: move |event| log_event(Event::KeyPress(event)),
+                onkeydown: move |event| log_event(event.data()),
+                onkeyup: move |event| log_event(event.data()),
+                onkeypress: move |event| log_event(event.data()),
 
-                onfocusin: move |event| log_event(Event::FocusIn(event)),
-                onfocusout: move |event| log_event(Event::FocusOut(event)),
+                onfocusin: move |event| log_event(event.data()),
+                onfocusout: move |event| log_event(event.data()),
 
                 "Hover, click, type or scroll to see the info down below"
             }
@@ -82,5 +63,5 @@ fn app(cx: Scope) -> Element {
                 }
             }
         }
-    ))
+    }
 }

+ 25 - 14
examples/assets/calculator.css

@@ -1,13 +1,17 @@
 html {
   box-sizing: border-box;
 }
-*, *:before, *:after {
+
+*,
+*:before,
+*:after {
   box-sizing: inherit;
 }
 
 body {
   margin: 0;
   font: 100 14px 'Roboto';
+  font-family: Arial;
   overflow: hidden;
 }
 
@@ -20,18 +24,18 @@ button {
   user-select: none;
   cursor: pointer;
   outline: none;
-  
-  -webkit-tap-highlight-color: rgba(0,0,0,0);
+
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
 }
 
 button:active {
-  box-shadow: inset 0px 0px 80px 0px rgba(0,0,0,0.25);
+  box-shadow: inset 0px 0px 80px 0px rgba(0, 0, 0, 0.25);
 }
 
 #wrapper {
   /* height: 100vh; */
   height: max-content;
-  
+
   display: flex;
   align-items: center;
   justify-content: center;
@@ -47,7 +51,7 @@ button:active {
   width: 100%;
   height: 100%;
   background: black;
-  
+
   display: flex;
   flex-direction: column;
 }
@@ -61,14 +65,14 @@ button:active {
   background: #1c191c;
   line-height: 130px;
   /* font-size: 6em; */
-    font-size: 16px;
-    font-size: 4vw;
+  font-size: 16px;
+  font-size: 4vw;
 
 
   max-height: 160px;
   padding: 0 30px;
   /* height: 80px; */
-  
+
   flex: 1;
 }
 
@@ -85,7 +89,7 @@ button:active {
 
 .calculator-keypad {
   height: 400px;
-  
+
   display: flex;
 }
 
@@ -99,7 +103,7 @@ button:active {
 
 .calculator .digit-keys {
   background: #e0e0e7;
-  
+
   display: flex;
   flex-direction: row;
   flex-wrap: wrap-reverse;
@@ -109,28 +113,34 @@ button:active {
   width: 80px;
   height: 80px;
   border-top: 1px solid #777;
-  border-right: 1px solid #666;  
+  border-right: 1px solid #666;
   text-align: center;
   line-height: 80px;
 }
+
 .calculator .function-keys .calculator-key {
   font-size: 2em;
 }
+
 .calculator .function-keys .key-multiply {
   line-height: 50px;
 }
+
 .calculator .digit-keys .calculator-key {
   font-size: 2.25em;
 }
+
 .calculator .digit-keys .key-0 {
   width: 160px;
   text-align: left;
   padding-left: 32px;
 }
+
 .calculator .digit-keys .key-dot {
   padding-top: 1em;
   font-size: 0.75em;
 }
+
 .calculator .operator-keys .calculator-key {
   color: white;
   border-right: 0;
@@ -138,8 +148,9 @@ button:active {
 }
 
 .calculator .function-keys {
-  background: linear-gradient(to bottom, rgba(202,202,204,1) 0%, rgba(196,194,204,1) 100%);
+  background: linear-gradient(to bottom, rgba(202, 202, 204, 1) 0%, rgba(196, 194, 204, 1) 100%);
 }
+
 .calculator .operator-keys {
-  background:  linear-gradient(to bottom, rgba(252,156,23,1) 0%, rgba(247,126,27,1) 100%);
+  background: linear-gradient(to bottom, rgba(252, 156, 23, 1) 0%, rgba(247, 126, 27, 1) 100%);
 }

+ 55 - 0
examples/backgrounded_futures.rs

@@ -0,0 +1,55 @@
+use dioxus::prelude::*;
+
+fn main() {
+    launch_desktop(app);
+}
+
+fn app() -> Element {
+    let mut show_child = use_signal(|| true);
+    let mut count = use_signal(|| 0);
+
+    let child = use_memo(move || {
+        rsx! {
+            Child {
+                count
+            }
+        }
+    });
+
+    rsx! {
+        button { onclick: move |_| show_child.toggle(), "Toggle child" }
+        button { onclick: move |_| count += 1, "Increment count" }
+        if show_child() {
+            {child.cloned()}
+        }
+    }
+}
+
+#[component]
+fn Child(count: Signal<i32>) -> Element {
+    let mut early_return = use_signal(|| false);
+
+    let early = rsx! {
+        button { onclick: move |_| early_return.toggle(), "Toggle {early_return} early return" }
+    };
+
+    if early_return() {
+        return early;
+    }
+
+    use_future(move || async move {
+        loop {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+            println!("Child")
+        }
+    });
+
+    use_effect(move || {
+        println!("Child count: {}", count());
+    });
+
+    rsx! {
+        "hellO!"
+        {early}
+    }
+}

+ 0 - 73
examples/borrowed.rs

@@ -1,73 +0,0 @@
-#![allow(non_snake_case)]
-
-/*
-Dioxus manages borrow lifetimes for you. This means any child may borrow from its parent. However, it is not possible
-to hand out an &mut T to children - all props are consumed by &P, so you'd only get an &&mut T.
-
-How does it work?
-
-Dioxus will manually drop closures and props - things that borrow data before the component is ran again. This is done
-"bottom up" from the lowest child all the way to the initiating parent. As it traverses each listener and prop, the
-drop implementation is manually called, freeing any memory and ensuring that memory is not leaked.
-
-We cannot drop from the parent to the children - if the drop implementation modifies the data, downstream references
-might be broken since we take an &mut T and and &T to the data. Instead, we work bottom up, making sure to remove any
-potential references to the data before finally giving out an &mut T. This prevents us from mutably aliasing the data,
-and is proven to be safe with MIRI.
-*/
-
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let text = cx.use_hook(|| vec![String::from("abc=def")]);
-
-    let first = text.get_mut(0).unwrap();
-
-    cx.render(rsx! {
-        div {
-            Child1 { text: first }
-        }
-    })
-}
-
-#[derive(Props)]
-struct C1Props<'a> {
-    text: &'a mut String,
-}
-
-fn Child1<'a>(cx: Scope<'a, C1Props<'a>>) -> Element {
-    let (left, right) = cx.props.text.split_once('=').unwrap();
-
-    cx.render(rsx! {
-        div {
-            Child2 { text: left  }
-            Child2 { text: right  }
-        }
-    })
-}
-
-#[derive(Props)]
-struct C2Props<'a> {
-    text: &'a str,
-}
-
-fn Child2<'a>(cx: Scope<'a, C2Props<'a>>) -> Element {
-    cx.render(rsx! {
-        Child3 { text: cx.props.text }
-    })
-}
-
-#[derive(Props)]
-struct C3Props<'a> {
-    text: &'a str,
-}
-
-fn Child3<'a>(cx: Scope<'a, C3Props<'a>>) -> Element {
-    cx.render(rsx! {
-        div { "{cx.props.text}"}
-    })
-}

+ 56 - 57
examples/calculator.rs

@@ -6,65 +6,58 @@ This calculator version uses React-style state management. All state is held as
 use dioxus::events::*;
 use dioxus::html::input_data::keyboard_types::Key;
 use dioxus::prelude::*;
-use dioxus_desktop::{Config, LogicalSize, WindowBuilder};
 
 fn main() {
-    let config = Config::new().with_window(
-        WindowBuilder::default()
-            .with_title("Calculator")
-            .with_inner_size(LogicalSize::new(300.0, 500.0)),
-    );
-
-    dioxus_desktop::launch_cfg(app, config);
+    LaunchBuilder::new()
+        .with_cfg(desktop!({
+            use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
+            Config::new().with_window(
+                WindowBuilder::default()
+                    .with_title("Calculator")
+                    .with_inner_size(LogicalSize::new(300.0, 525.0)),
+            )
+        }))
+        .launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let val = use_state(cx, || String::from("0"));
+fn app() -> Element {
+    let mut val = use_signal(|| String::from("0"));
 
-    let input_digit = move |num: u8| {
-        if val.get() == "0" {
+    let mut input_digit = move |num: String| {
+        if val() == "0" {
             val.set(String::new());
         }
-
-        val.make_mut().push_str(num.to_string().as_str());
+        val.write().push_str(num.as_str());
     };
 
-    let input_operator = move |key: &str| val.make_mut().push_str(key);
+    let mut input_operator = move |key: &str| val.write().push_str(key);
 
     let handle_key_down_event = move |evt: KeyboardEvent| match evt.key() {
         Key::Backspace => {
-            if !val.len() != 0 {
-                val.make_mut().pop();
+            if !val().is_empty() {
+                val.write().pop();
             }
         }
         Key::Character(character) => match character.as_str() {
-            "+" => input_operator("+"),
-            "-" => input_operator("-"),
-            "/" => input_operator("/"),
-            "*" => input_operator("*"),
-            "0" => input_digit(0),
-            "1" => input_digit(1),
-            "2" => input_digit(2),
-            "3" => input_digit(3),
-            "4" => input_digit(4),
-            "5" => input_digit(5),
-            "6" => input_digit(6),
-            "7" => input_digit(7),
-            "8" => input_digit(8),
-            "9" => input_digit(9),
+            "+" | "-" | "/" | "*" => input_operator(&character),
+            "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" => input_digit(character),
             _ => {}
         },
         _ => {}
     };
 
-    cx.render(rsx!(
+    rsx! {
         style { {include_str!("./assets/calculator.css")} }
         div { id: "wrapper",
             div { class: "app",
-                div { class: "calculator",
-                    tabindex: "0",
-                    onkeydown: handle_key_down_event,
-                    div { class: "calculator-display", "{val}" }
+                div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event,
+                    div { class: "calculator-display",
+                        if val().is_empty() {
+                            "0"
+                        } else {
+                            "{val}"
+                        }
+                    }
                     div { class: "calculator-keypad",
                         div { class: "input-keys",
                             div { class: "function-keys",
@@ -72,55 +65,62 @@ fn app(cx: Scope) -> Element {
                                     class: "calculator-key key-clear",
                                     onclick: move |_| {
                                         val.set(String::new());
-                                        if !val.is_empty(){
+                                        if !val.cloned().is_empty() {
                                             val.set("0".into());
                                         }
                                     },
-                                    if val.is_empty() { "C" } else { "AC" }
+                                    if val.cloned().is_empty() { "C" } else { "AC" }
                                 }
                                 button {
                                     class: "calculator-key key-sign",
                                     onclick: move |_| {
-                                        let temp = calc_val(val.as_str());
-                                        if temp > 0.0 {
-                                            val.set(format!("-{temp}"));
+                                        let new_val = calc_val(val.cloned().as_str());
+                                        if new_val > 0.0 {
+                                            val.set(format!("-{new_val}"));
                                         } else {
-                                            val.set(format!("{}", temp.abs()));
+                                            val.set(format!("{}", new_val.abs()));
                                         }
                                     },
                                     "±"
                                 }
                                 button {
                                     class: "calculator-key key-percent",
-                                    onclick: move |_| {
-                                        val.set(
-                                            format!("{}", calc_val(val.as_str()) / 100.0)
-                                        );
-                                    },
+                                    onclick: move |_| val.set(format!("{}", calc_val(val.cloned().as_str()) / 100.0)),
                                     "%"
                                 }
                             }
                             div { class: "digit-keys",
-                                button { class: "calculator-key key-0", onclick: move |_| input_digit(0), "0" }
-                                button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'), "●" }
+                                button {
+                                    class: "calculator-key key-0",
+                                    onclick: move |_| input_digit(0.to_string()),
+                                    "0"
+                                }
+                                button {
+                                    class: "calculator-key key-dot",
+                                    onclick: move |_| val.write().push('.'),
+                                    "●"
+                                }
                                 for k in 1..10 {
                                     button {
                                         class: "calculator-key {k}",
                                         name: "key-{k}",
-                                        onclick: move |_| input_digit(k),
+                                        onclick: move |_| input_digit(k.to_string()),
                                         "{k}"
                                     }
                                 }
                             }
                         }
                         div { class: "operator-keys",
-                            button { class: "calculator-key key-divide", onclick: move |_| input_operator("/"), "÷" }
-                            button { class: "calculator-key key-multiply", onclick: move |_| input_operator("*"), "×" }
-                            button { class: "calculator-key key-subtract", onclick: move |_| input_operator("-"), "−" }
-                            button { class: "calculator-key key-add", onclick: move |_| input_operator("+"), "+" }
+                            for (key, class) in [("/", "key-divide"), ("*", "key-multiply"), ("-", "key-subtract"), ("+", "key-add")] {
+                                button {
+                                    class: "calculator-key {class}",
+                                    onclick: move |_| input_operator(key),
+                                    "{key}"
+                                }
+                            }
                             button {
                                 class: "calculator-key key-equals",
-                                onclick: move |_| val.set(format!("{}", calc_val(val.as_str()))),
+                                onclick: move |_| val.set(format!("{}", calc_val(val.cloned().as_str()))),
                                 "="
                             }
                         }
@@ -128,8 +128,7 @@ fn app(cx: Scope) -> Element {
                 }
             }
         }
-
-    ))
+    }
 }
 
 fn calc_val(val: &str) -> f64 {

+ 22 - 56
examples/pattern_model.rs → examples/calculator_mutable.rs

@@ -17,12 +17,12 @@
 //! the RefCell will panic and crash. You can use `try_get_mut` or `.modify` to avoid this problem, or just not hold two
 //! RefMuts at the same time.
 
+use dioxus::desktop::tao::dpi::LogicalSize;
+use dioxus::desktop::{Config, WindowBuilder};
 use dioxus::events::*;
 use dioxus::html::input_data::keyboard_types::Key;
 use dioxus::html::MouseEvent;
 use dioxus::prelude::*;
-use dioxus_desktop::tao::dpi::LogicalSize;
-use dioxus_desktop::{Config, WindowBuilder};
 
 fn main() {
     let cfg = Config::new().with_window(
@@ -32,50 +32,34 @@ fn main() {
             .with_inner_size(LogicalSize::new(320.0, 530.0)),
     );
 
-    dioxus_desktop::launch_cfg(app, cfg);
+    LaunchBuilder::desktop().with_cfg(cfg).launch(app);
 }
 
 const STYLE: &str = include_str!("./assets/calculator.css");
 
-fn app(cx: Scope) -> Element {
-    let state = use_ref(cx, Calculator::new);
+fn app() -> Element {
+    let mut state = use_signal(Calculator::new);
 
-    cx.render(rsx! {
+    rsx! {
         style { {STYLE} }
         div { id: "wrapper",
             div { class: "app",
-                div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
+                div {
+                    class: "calculator",
+                    onkeypress: move |evt| state.write().handle_keydown(evt),
                     div { class: "calculator-display", {state.read().formatted_display()} }
                     div { class: "calculator-keypad",
                         div { class: "input-keys",
                             div { class: "function-keys",
-                                CalculatorKey {
-                                    name: "key-clear",
-                                    onclick: move |_| state.write().clear_display(),
+                                CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(),
                                     if state.read().display_value == "0" { "C" } else { "AC" }
                                 }
-                                CalculatorKey {
-                                    name: "key-sign",
-                                    onclick: move |_| state.write().toggle_sign(),
-                                    "±"
-                                }
-                                CalculatorKey {
-                                    name: "key-percent",
-                                    onclick: move |_| state.write().toggle_percent(),
-                                    "%"
-                                }
+                                CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±" }
+                                CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%" }
                             }
                             div { class: "digit-keys",
-                                CalculatorKey {
-                                    name: "key-0",
-                                    onclick: move |_| state.write().input_digit(0),
-                                    "0"
-                                }
-                                CalculatorKey {
-                                    name: "key-dot",
-                                    onclick: move |_|  state.write().input_dot(),
-                                    "●"
-                                }
+                                CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" }
+                                CalculatorKey { name: "key-dot", onclick: move |_| state.write().input_dot(), "●" }
                                 for k in 1..10 {
                                     CalculatorKey {
                                         key: "{k}",
@@ -102,39 +86,21 @@ fn app(cx: Scope) -> Element {
                                 onclick: move |_| state.write().set_operator(Operator::Sub),
                                 "−"
                             }
-                            CalculatorKey {
-                                name: "key-add",
-                                onclick: move |_| state.write().set_operator(Operator::Add),
-                                "+"
-                            }
-                            CalculatorKey {
-                                name: "key-equals",
-                                onclick: move |_| state.write().perform_operation(),
-                                "="
-                            }
+                            CalculatorKey { name: "key-add", onclick: move |_| state.write().set_operator(Operator::Add), "+" }
+                            CalculatorKey { name: "key-equals", onclick: move |_| state.write().perform_operation(), "=" }
                         }
                     }
                 }
             }
         }
-    })
-}
-
-#[derive(Props)]
-struct CalculatorKeyProps<'a> {
-    name: &'a str,
-    onclick: EventHandler<'a, MouseEvent>,
-    children: Element<'a>,
+    }
 }
 
-fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
-    cx.render(rsx! {
-        button {
-            class: "calculator-key {cx.props.name}",
-            onclick: move |e| cx.props.onclick.call(e),
-            {&cx.props.children}
-        }
-    })
+#[component]
+fn CalculatorKey(name: String, onclick: EventHandler<MouseEvent>, children: Element) -> Element {
+    rsx! {
+        button { class: "calculator-key {name}", onclick: move |e| onclick.call(e), {&children} }
+    }
 }
 
 struct Calculator {

+ 0 - 22
examples/callback.rs

@@ -1,22 +0,0 @@
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let login = use_callback!(cx, move |_| async move {
-        let res = reqwest::get("https://dog.ceo/api/breeds/list/all")
-            .await
-            .unwrap()
-            .text()
-            .await
-            .unwrap();
-
-        println!("{res:#?}, ");
-    });
-
-    cx.render(rsx! {
-        button { onclick: login, "Click me!" }
-    })
-}

+ 10 - 8
examples/clock.rs

@@ -1,22 +1,24 @@
 use dioxus::prelude::*;
-use dioxus_signals::use_signal;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let mut count = use_signal(cx, || 0);
+fn app() -> Element {
+    let mut count = use_signal(|| 0);
 
-    use_future!(cx, || async move {
+    use_future(move || async move {
         loop {
             tokio::time::sleep(std::time::Duration::from_millis(10)).await;
             count += 1;
-            println!("current: {count}");
         }
     });
 
-    cx.render(rsx! {
+    use_effect(move || {
+        println!("High-Five counter: {}", count());
+    });
+
+    rsx! {
         div { "High-Five counter: {count}" }
-    })
+    }
 }

+ 32 - 38
examples/compose.rs

@@ -1,68 +1,62 @@
 //! This example shows how to create a popup window and send data back to the parent window.
 
+use std::rc::Rc;
+
 use dioxus::prelude::*;
 use futures_util::StreamExt;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let emails_sent = use_ref(cx, Vec::new);
+fn app() -> Element {
+    let mut emails_sent = use_signal(|| Vec::new() as Vec<String>);
 
-    let tx = use_coroutine(cx, |mut rx: UnboundedReceiver<String>| {
-        to_owned![emails_sent];
-        async move {
-            while let Some(message) = rx.next().await {
-                emails_sent.write().push(message);
-            }
+    // Wait for responses to the compose channel, and then push them to the emails_sent signal.
+    let handle = use_coroutine(|mut rx: UnboundedReceiver<String>| async move {
+        while let Some(message) = rx.next().await {
+            emails_sent.write().push(message);
         }
     });
 
-    cx.render(rsx! {
-        div {
-            h1 { "This is your email" }
-
-            button {
-                onclick: move |_| {
-                    let dom = VirtualDom::new_with_props(compose, ComposeProps { app_tx: tx.clone() });
-                    dioxus_desktop::window().new_window(dom, Default::default());
-                },
-                "Click to compose a new email"
-            }
-
-            ul {
-                for message in emails_sent.read().iter() {
-                    li {
-                        h3 { "email" }
-                        span {"{message}"}
-                    }
+    let open_compose_window = move |_evt: MouseEvent| {
+        let tx = handle.tx();
+        dioxus::desktop::window().new_window(
+            VirtualDom::new_with_props(compose, Rc::new(move |s| tx.unbounded_send(s).unwrap())),
+            Default::default(),
+        );
+    };
+
+    rsx! {
+        h1 { "This is your email" }
+        button { onclick: open_compose_window, "Click to compose a new email" }
+        ul {
+            for message in emails_sent.read().iter() {
+                li {
+                    h3 { "email" }
+                    span { "{message}" }
                 }
             }
         }
-    })
-}
-
-struct ComposeProps {
-    app_tx: Coroutine<String>,
+    }
 }
 
-fn compose(cx: Scope<ComposeProps>) -> Element {
-    let user_input = use_state(cx, String::new);
+fn compose(send: Rc<dyn Fn(String)>) -> Element {
+    let mut user_input = use_signal(String::new);
 
-    cx.render(rsx! {
+    rsx! {
         div {
             h1 { "Compose a new email" }
 
             button {
                 onclick: move |_| {
-                    cx.props.app_tx.send(user_input.get().clone());
-                    dioxus_desktop::window().close();
+                    send(user_input.cloned());
+                    dioxus::desktop::window().close();
                 },
                 "Click to send"
             }
 
             input { oninput: move |e| user_input.set(e.value()), value: "{user_input}" }
         }
-    })
+    }
 }

+ 22 - 17
examples/control_focus.rs

@@ -3,36 +3,41 @@ use std::rc::Rc;
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let elements: &UseRef<Vec<Rc<MountedData>>> = use_ref(cx, Vec::new);
-    let running = use_state(cx, || true);
+fn app() -> Element {
+    let mut elements = use_signal(Vec::<Rc<MountedData>>::new);
+    let mut running = use_signal(|| true);
 
-    use_future!(cx, |(elements, running)| async move {
+    use_future(move || async move {
         let mut focused = 0;
-        if *running.current() {
-            loop {
-                tokio::time::sleep(std::time::Duration::from_millis(10)).await;
-                if let Some(element) = elements.with(|f| f.get(focused).cloned()) {
-                    _ = element.set_focus(true).await;
-                } else {
-                    focused = 0;
-                }
-                focused += 1;
+
+        loop {
+            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
+
+            if !running() {
+                continue;
             }
+
+            if let Some(element) = elements.with(|f| f.get(focused).cloned()) {
+                _ = element.set_focus(true).await;
+            } else {
+                focused = 0;
+            }
+
+            focused += 1;
         }
     });
 
-    cx.render(rsx!(
+    rsx! {
         div {
             h1 { "Input Roulette" }
             for i in 0..100 {
                 input {
                     value: "{i}",
                     onmounted: move |cx| {
-                        elements.write().push(cx.inner().clone());
+                        elements.write().push(cx.data());
                     },
                     oninput: move |_| {
                         running.set(false);
@@ -40,5 +45,5 @@ fn app(cx: Scope) -> Element {
                 }
             }
         }
-    ))
+    }
 }

+ 36 - 19
examples/counter.rs

@@ -4,33 +4,50 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let counters = use_state(cx, || vec![0, 0, 0]);
-    let sum: usize = counters.iter().copied().sum();
+fn app() -> Element {
+    let mut counters = use_signal(|| vec![0, 0, 0]);
+    let sum = use_memo(move || counters.read().iter().copied().sum::<i32>());
 
-    render! {
+    rsx! {
         div {
-            button { onclick: move |_| counters.make_mut().push(0), "Add counter" }
-            button { onclick: move |_| { counters.make_mut().pop(); }, "Remove counter" }
+            button { onclick: move |_| counters.write().push(0), "Add counter" }
+            button {
+                onclick: move |_| {
+                    counters.write().pop();
+                },
+                "Remove counter"
+            }
             p { "Total: {sum}" }
-            for (i, counter) in counters.iter().enumerate() {
-                li {
-                    button { onclick: move |_| counters.make_mut()[i] -= 1, "-1" }
-                    input {
-                        value: "{counter}",
-                        oninput: move |e| {
-                            if let Ok(value) = e.value().parse::<usize>() {
-                                counters.make_mut()[i] = value;
-                            }
-                        }
+            for i in 0..counters.len() {
+                Child { i, counters }
+            }
+        }
+    }
+}
+
+#[component]
+fn Child(counters: Signal<Vec<i32>>, i: usize) -> Element {
+    rsx! {
+        li {
+            button { onclick: move |_| counters.write()[i] -= 1, "-1" }
+            input {
+                value: "{counters.read()[i]}",
+                oninput: move |e| {
+                    if let Ok(value) = e.value().parse::<i32>() {
+                        counters.write()[i] = value;
                     }
-                    button { onclick: move |_| counters.make_mut()[i] += 1, "+1" }
-                    button { onclick: move |_| { counters.make_mut().remove(i); }, "x" }
                 }
             }
+            button { onclick: move |_| counters.write()[i] += 1, "+1" }
+            button {
+                onclick: move |_| {
+                    counters.write().remove(i);
+                },
+                "x"
+            }
         }
     }
 }

+ 77 - 93
examples/crm.rs

@@ -1,112 +1,100 @@
 //! Tiny CRM: A port of the Yew CRM example to Dioxus.
 use dioxus::prelude::*;
-use dioxus_router::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(App);
+    LaunchBuilder::new()
+        .with_cfg(desktop!({
+            use dioxus::desktop::{LogicalSize, WindowBuilder};
+            dioxus::desktop::Config::default()
+                .with_window(WindowBuilder::new().with_inner_size(LogicalSize::new(800, 600)))
+        }))
+        .launch(|| {
+            rsx! {
+                link {
+                    rel: "stylesheet",
+                    href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css",
+                    integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5",
+                    crossorigin: "anonymous"
+                }
+                style { {r#" .red { background-color: rgb(202, 60, 60) !important; } "#} }
+                h1 { "Dioxus CRM Example" }
+                Router::<Route> {}
+            }
+        });
 }
 
-#[derive(Routable, Clone)]
-#[rustfmt::skip]
-enum Route {
-    #[route("/")]
-    ClientList {},
-    #[route("/new")]
-    ClientAdd {},
-    #[route("/settings")]
-    Settings {},
-}
+/// We only have one list of clients for the whole app, so we can use a global signal.
+static CLIENTS: GlobalSignal<Vec<Client>> = Signal::global(Vec::new);
 
-#[derive(Clone, Debug, Default)]
-pub struct Client {
-    pub first_name: String,
-    pub last_name: String,
-    pub description: String,
+struct Client {
+    first_name: String,
+    last_name: String,
+    description: String,
 }
 
-type ClientContext = Vec<Client>;
-
-#[component]
-fn App(cx: Scope) -> Element {
-    use_shared_state_provider::<ClientContext>(cx, Default::default);
-
-    render! {
-        link {
-            rel: "stylesheet",
-            href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css",
-            integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5",
-            crossorigin: "anonymous"
-        }
-
-        style {
-            "
-            .red {{
-                background-color: rgb(202, 60, 60) !important;
-            }}
-        "
-        }
+#[derive(Routable, Clone)]
+enum Route {
+    #[route("/")]
+    ClientList,
 
-        h1 { "Dioxus CRM Example" }
+    #[route("/new")]
+    ClientAdd,
 
-        Router::<Route> {}
-    }
+    #[route("/settings")]
+    Settings,
 }
 
 #[component]
-fn ClientList(cx: Scope) -> Element {
-    let clients = use_shared_state::<ClientContext>(cx).unwrap();
-
-    cx.render(rsx! {
+fn ClientList() -> Element {
+    rsx! {
         h2 { "List of Clients" }
-
-        Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" }
-        Link { to: Route::Settings {}, class: "pure-button", "Settings" }
-
-        for client in clients.read().iter() {
-            div {
-                class: "client",
-                style: "margin-bottom: 50px",
-
+        Link { to: Route::ClientAdd, class: "pure-button pure-button-primary", "Add Client" }
+        Link { to: Route::Settings, class: "pure-button", "Settings" }
+        for client in CLIENTS.read().iter() {
+            div { class: "client", style: "margin-bottom: 50px",
                 p { "Name: {client.first_name} {client.last_name}" }
                 p { "Description: {client.description}" }
             }
         }
-    })
+    }
 }
 
 #[component]
-fn ClientAdd(cx: Scope) -> Element {
-    let clients = use_shared_state::<ClientContext>(cx).unwrap();
-    let first_name = use_state(cx, String::new);
-    let last_name = use_state(cx, String::new);
-    let description = use_state(cx, String::new);
-
-    cx.render(rsx! {
+fn ClientAdd() -> Element {
+    let mut first_name = use_signal(String::new);
+    let mut last_name = use_signal(String::new);
+    let mut description = use_signal(String::new);
+
+    let submit_client = move |_: FormEvent| {
+        // Write the client
+        CLIENTS.write().push(Client {
+            first_name: first_name(),
+            last_name: last_name(),
+            description: description(),
+        });
+
+        // And then navigate back to the client list
+        dioxus::router::router().push(Route::ClientList);
+    };
+
+    rsx! {
         h2 { "Add new Client" }
-
-        form {
-            class: "pure-form pure-form-aligned",
-            onsubmit: move |_| {
-                let mut clients = clients.write();
-                clients
-                    .push(Client {
-                        first_name: first_name.to_string(),
-                        last_name: last_name.to_string(),
-                        description: description.to_string(),
-                    });
-                dioxus_router::router().push(Route::ClientList {});
-            },
-
+        form { class: "pure-form pure-form-aligned", onsubmit: submit_client,
             fieldset {
                 div { class: "pure-control-group",
-                    label { "for": "first_name", "First Name" }
+                    label { r#for: "first_name", "First Name" }
                     input {
                         id: "first_name",
-                        "type": "text",
+                        r#type: "text",
                         placeholder: "First Name…",
                         required: "",
                         value: "{first_name}",
-                        oninput: move |e| first_name.set(e.value())
+                        oninput: move |e| first_name.set(e.value()),
+
+                        // when the form mounts, focus the first name input
+                        onmounted: move |e| async move {
+                            _ = e.set_focus(true).await;
+                        },
                     }
                 }
 
@@ -114,7 +102,7 @@ fn ClientAdd(cx: Scope) -> Element {
                     label { "for": "last_name", "Last Name" }
                     input {
                         id: "last_name",
-                        "type": "text",
+                        r#type: "text",
                         placeholder: "Last Name…",
                         required: "",
                         value: "{last_name}",
@@ -133,30 +121,26 @@ fn ClientAdd(cx: Scope) -> Element {
                 }
 
                 div { class: "pure-controls",
-                    button { "type": "submit", class: "pure-button pure-button-primary", "Save" }
-                    Link { to: Route::ClientList {}, class: "pure-button pure-button-primary red", "Cancel" }
+                    button { r#type: "submit", class: "pure-button pure-button-primary", "Save" }
+                    Link { to: Route::ClientList, class: "pure-button pure-button-primary red", "Cancel" }
                 }
             }
         }
-    })
+    }
 }
 
 #[component]
-fn Settings(cx: Scope) -> Element {
-    let clients = use_shared_state::<ClientContext>(cx).unwrap();
-
-    cx.render(rsx! {
+fn Settings() -> Element {
+    rsx! {
         h2 { "Settings" }
-
         button {
             class: "pure-button pure-button-primary red",
             onclick: move |_| {
-                let mut clients = clients.write();
-                clients.clear();
+                CLIENTS.write().clear();
+                dioxus::router::router().push(Route::ClientList);
             },
             "Remove all Clients"
         }
-
-        Link { to: Route::ClientList {}, class: "pure-button", "Go back" }
-    })
+        Link { to: Route::ClientList, class: "pure-button", "Go back" }
+    }
 }

+ 13 - 8
examples/custom_assets.rs

@@ -1,16 +1,21 @@
 use dioxus::prelude::*;
 
+#[cfg(not(feature = "collect-assets"))]
+static ASSET_PATH: &str = "examples/assets/logo.png";
+
+#[cfg(feature = "collect-assets")]
+static ASSET_PATH: &str =
+    manganis::mg!(image("examples/assets/logo.png").format(ImageType::Avif)).path();
+
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         div {
-            p {
-                "This should show an image:"
-            }
-            img { src: manganis::mg!(image("examples/assets/logo.png").format(ImageType::Avif)).to_string() }
+            p { "This should show an image:" }
+            img { src: ASSET_PATH.to_string() }
         }
-    })
+    }
 }

+ 18 - 18
examples/custom_html.rs

@@ -1,19 +1,20 @@
 //! This example shows how to use a custom index.html and custom <HEAD> extensions
 //! to add things like stylesheets, scripts, and third-party JS libraries.
 
+use dioxus::desktop::Config;
 use dioxus::prelude::*;
-use dioxus_desktop::Config;
 
 fn main() {
-    dioxus_desktop::launch_cfg(
-        app,
-        Config::new().with_custom_head("<style>body { background-color: red; }</style>".into()),
-    );
+    LaunchBuilder::desktop()
+        .with_cfg(
+            Config::new().with_custom_head("<style>body { background-color: red; }</style>".into()),
+        )
+        .launch(app);
 
-    dioxus_desktop::launch_cfg(
-        app,
-        Config::new().with_custom_index(
-            r#"
+    LaunchBuilder::desktop()
+        .with_cfg(
+            Config::new().with_custom_index(
+                r#"
 <!DOCTYPE html>
 <html>
   <head>
@@ -26,15 +27,14 @@ fn main() {
   </body>
 </html>
         "#
-            .into(),
-        ),
-    );
+                .into(),
+            ),
+        )
+        .launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
-        div {
-            h1 {"hello world!"}
-        }
-    })
+fn app() -> Element {
+    rsx! {
+        div { h1 { "hello world!" } }
+    }
 }

+ 8 - 12
examples/disabled.rs

@@ -1,25 +1,21 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let disabled = use_state(cx, || false);
+fn app() -> Element {
+    let mut disabled = use_signal(|| false);
 
-    cx.render(rsx! {
+    rsx! {
         div {
-            button {
-                onclick: move |_| disabled.set(!disabled),
+            button { onclick: move |_| disabled.toggle(),
                 "click to "
-                if disabled == true { "enable" } else { "disable" }
+                if disabled() { "enable" } else { "disable" }
                 " the lower button"
             }
 
-            button {
-                disabled: "{disabled}",
-                "lower button"
-            }
+            button { disabled, "lower button" }
         }
-    })
+    }
 }

+ 46 - 55
examples/dog_app.rs

@@ -2,57 +2,49 @@ use dioxus::prelude::*;
 use std::collections::HashMap;
 
 fn main() {
-    dioxus_desktop::launch(|cx| render!(AppRoot {}));
+    launch(app);
 }
 
-#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
-struct ListBreeds {
-    message: HashMap<String, Vec<String>>,
-}
-
-#[component]
-fn AppRoot(cx: Scope<'_>) -> Element {
-    let breed = use_state(cx, || "deerhound".to_string());
-
-    let breeds = use_future!(cx, || async move {
-        reqwest::get("https://dog.ceo/api/breeds/list/all")
+fn app() -> Element {
+    let mut breed = use_signal(|| "deerhound".to_string());
+    let breed_list = use_resource(move || async move {
+        let list = reqwest::get("https://dog.ceo/api/breeds/list/all")
             .await
             .unwrap()
             .json::<ListBreeds>()
-            .await
-    });
+            .await;
+
+        let Ok(breeds) = list else {
+            return rsx! { "error fetching breeds" };
+        };
 
-    match breeds.value()? {
-        Ok(breed_list) => cx.render(rsx! {
-            div { height: "500px",
-                h1 { "Select a dog breed!" }
-                div { display: "flex",
-                    ul { flex: "50%",
-                        for cur_breed in breed_list.message.keys().take(10) {
-                            li { key: "{cur_breed}",
-                                button {
-                                    onclick: move |_| breed.set(cur_breed.clone()),
-                                    "{cur_breed}"
-                                }
-                            }
-                        }
+        rsx! {
+            for cur_breed in breeds.message.keys().take(10).cloned() {
+                li { key: "{cur_breed}",
+                    button { onclick: move |_| breed.set(cur_breed.clone()),
+                        "{cur_breed}"
                     }
-                    div { flex: "50%", BreedPic { breed: breed.to_string() } }
                 }
             }
-        }),
-        Err(_e) => cx.render(rsx! { div { "Error fetching breeds" } }),
-    }
-}
+        }
+    });
 
-#[derive(serde::Deserialize, Debug)]
-struct DogApi {
-    message: String,
+    let Some(breed_list) = breed_list() else {
+        return rsx! { "loading breeds..." };
+    };
+
+    rsx! {
+        h1 { "Select a dog breed!" }
+        div { height: "500px", display: "flex",
+            ul { flex: "50%", {breed_list} }
+            div { flex: "50%", BreedPic { breed } }
+        }
+    }
 }
 
 #[component]
-fn BreedPic(cx: Scope, breed: String) -> Element {
-    let fut = use_future!(cx, |breed| async move {
+fn BreedPic(breed: Signal<String>) -> Element {
+    let mut fut = use_resource(move || async move {
         reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
             .await
             .unwrap()
@@ -60,23 +52,22 @@ fn BreedPic(cx: Scope, breed: String) -> Element {
             .await
     });
 
-    match fut.value()? {
-        Ok(resp) => render! {
-            div {
-                button {
-                    onclick: move |_| {
-                        println!("clicked");
-                        fut.restart()
-                    },
-                    "Click to fetch another doggo"
-                }
-                img {
-                    src: "{resp.message}",
-                    max_width: "500px",
-                    max_height: "500px",
-                }
-            }
+    match fut.read().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}" }
         },
-        Err(_) => render! { div { "loading dogs failed" } },
+        Some(Err(_)) => rsx! { "loading image failed" },
+        None => rsx! { "loading image..." },
     }
 }
+
+#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
+struct ListBreeds {
+    message: HashMap<String, Vec<String>>,
+}
+
+#[derive(serde::Deserialize, Debug)]
+struct DogApi {
+    message: String,
+}

+ 0 - 36
examples/drops.rs

@@ -1,36 +0,0 @@
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let count = if cx.generation() % 2 == 0 { 10 } else { 0 };
-
-    println!("Generation: {}", cx.generation());
-
-    if cx.generation() < 10 {
-        cx.needs_update();
-    }
-
-    render! {
-        for _ in 0..count {
-            drop_child {}
-        }
-    }
-}
-
-fn drop_child(cx: Scope) -> Element {
-    cx.use_hook(|| Drops);
-    render! {
-        div{}
-    }
-}
-
-struct Drops;
-
-impl Drop for Drops {
-    fn drop(&mut self) {
-        println!("Dropped!");
-    }
-}

+ 7 - 9
examples/dynamic_asset.rs

@@ -1,23 +1,21 @@
+use dioxus::desktop::{use_asset_handler, wry::http::Response};
 use dioxus::prelude::*;
-use dioxus_desktop::{use_asset_handler, wry::http::Response};
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    use_asset_handler(cx, "logos", |request, response| {
-        // Note that the "logos" prefix is stripped from the URI
-        //
-        // However, the asset is absolute to its "virtual folder" - meaning it starts with a leading slash
-        if request.uri().path() != "/logo.png" {
+fn app() -> Element {
+    use_asset_handler("logos", |request, response| {
+        // We get the original path - make sure you handle that!
+        if request.uri().path() != "/logos/logo.png" {
             return;
         }
 
         response.respond(Response::new(include_bytes!("./assets/logo.png").to_vec()));
     });
 
-    render! {
+    rsx! {
         div {
             img { src: "/logos/logo.png" }
         }

+ 8 - 11
examples/error_handle.rs

@@ -1,28 +1,25 @@
-use dioxus::{core::CapturedError, prelude::*};
+use dioxus::{dioxus_core::CapturedError, prelude::*};
 
 fn main() {
-    dioxus_desktop::launch(App);
+    launch_desktop(app);
 }
 
-#[component]
-fn App(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         ErrorBoundary {
             handle_error: |error: CapturedError| rsx! {"Found error {error}"},
-            DemoC {
-                x: 1
-            }
+            DemoC { x: 1 }
         }
-    })
+    }
 }
 
 #[component]
-fn DemoC(cx: Scope, x: i32) -> Element {
+fn DemoC(x: i32) -> Element {
     let result = Err("Error");
 
     result.throw()?;
 
-    render! {
+    rsx! {
         h1 { "{x}" }
     }
 }

+ 7 - 11
examples/eval.rs

@@ -1,12 +1,12 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let future = use_future(cx, (), |_| async move {
-        let eval = eval(
+fn app() -> Element {
+    let future = use_resource(move || async move {
+        let mut eval = eval(
             r#"
                 dioxus.send("Hi from JS!");
                 let msg = await dioxus.recv();
@@ -22,12 +22,8 @@ fn app(cx: Scope) -> Element {
         res
     });
 
-    match future.value() {
-        Some(v) => cx.render(rsx!(
-            p { "{v}" }
-        )),
-        _ => cx.render(rsx!(
-            p { "hello" }
-        )),
+    match future.value().as_ref() {
+        Some(v) => rsx!( p { "{v}" } ),
+        _ => rsx!( p { "waiting.." } ),
     }
 }

+ 0 - 57
examples/fermi.rs

@@ -1,57 +0,0 @@
-#![allow(non_snake_case)]
-
-use dioxus::prelude::*;
-use fermi::*;
-
-fn main() {
-    dioxus_desktop::launch(app)
-}
-
-static NAME: Atom<String> = Atom(|_| "world".to_string());
-
-fn app(cx: Scope) -> Element {
-    use_init_atom_root(cx);
-    let name = use_read(cx, &NAME);
-
-    cx.render(rsx! {
-        div { "hello {name}!" }
-        Child {}
-        ChildWithRef {}
-    })
-}
-
-fn Child(cx: Scope) -> Element {
-    let set_name = use_set(cx, &NAME);
-
-    cx.render(rsx! {
-        button {
-            onclick: move |_| set_name("dioxus".to_string()),
-            "reset name"
-        }
-    })
-}
-
-static NAMES: AtomRef<Vec<String>> = AtomRef(|_| vec!["world".to_string()]);
-
-fn ChildWithRef(cx: Scope) -> Element {
-    let names = use_atom_ref(cx, &NAMES);
-
-    cx.render(rsx! {
-        div {
-            ul {
-                for name in names.read().iter() {
-                    li { "hello: {name}" }
-                }
-            }
-            button {
-                onclick: move |_| {
-                    let names = names.clone();
-                    cx.spawn(async move {
-                        names.write().push("asd".to_string());
-                    })
-                },
-                "Add name"
-            }
-        }
-    })
-}

+ 14 - 10
examples/file_explorer.rs

@@ -8,30 +8,34 @@
 //! It also uses `use_ref` to maintain a model, rather than `use_state`. That way,
 //! we dont need to clutter our code with `read` commands.
 
+use dioxus::desktop::{Config, WindowBuilder};
 use dioxus::prelude::*;
-use dioxus_desktop::{Config, WindowBuilder};
 
 fn main() {
-    dioxus_desktop::launch_cfg(
-        app,
-        Config::new().with_window(WindowBuilder::new().with_resizable(true)),
-    );
+    LaunchBuilder::desktop()
+        .with_cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true)))
+        .launch(app)
 }
 
+#[cfg(not(feature = "collect-assets"))]
+const _STYLE: &str = include_str!("../examples/assets/fileexplorer.css");
+
+#[cfg(feature = "collect-assets")]
 const _STYLE: &str = manganis::mg!(file("./examples/assets/fileexplorer.css"));
 
-fn app(cx: Scope) -> Element {
-    let files = use_ref(cx, Files::new);
+fn app() -> Element {
+    let mut files = use_signal(Files::new);
 
-    cx.render(rsx! {
+    rsx! {
         div {
-            link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", }
+            link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet" }
             header {
                 i { class: "material-icons icon-menu", "menu" }
                 h1 { "Files: ", {files.read().current()} }
                 span { }
                 i { class: "material-icons", onclick: move |_| files.write().go_up(), "logout" }
             }
+            style { "{_STYLE}" }
             main {
                 {files.read().path_names.iter().enumerate().map(|(dir_id, path)| {
                     let path_end = path.split('/').last().unwrap_or(path.as_str());
@@ -60,7 +64,7 @@ fn app(cx: Scope) -> Element {
                 }
             }
         }
-    })
+    }
 }
 
 struct Files {

+ 31 - 39
examples/file_upload.rs

@@ -4,21 +4,38 @@ use dioxus::prelude::*;
 use tokio::time::sleep;
 
 fn main() {
-    dioxus_desktop::launch(App);
+    launch(App);
 }
 
-fn App(cx: Scope) -> Element {
-    let enable_directory_upload = use_state(cx, || false);
-    let files_uploaded: &UseRef<Vec<String>> = use_ref(cx, Vec::new);
+fn App() -> Element {
+    let mut enable_directory_upload = use_signal(|| false);
+    let mut files_uploaded = use_signal(|| Vec::new() as Vec<String>);
 
-    cx.render(rsx! {
+    let upload_files = move |evt: FormEvent| async move {
+        for file_name in evt.files().unwrap().files() {
+            // no files on form inputs?
+            sleep(std::time::Duration::from_secs(1)).await;
+            files_uploaded.write().push(file_name);
+        }
+    };
+
+    let handle_file_drop = move |evt: DragEvent| async move {
+        if let Some(file_engine) = &evt.files() {
+            let files = file_engine.files();
+            for file_name in &files {
+                if let Some(file) = file_engine.read_file_to_string(file_name).await {
+                    files_uploaded.write().push(file);
+                }
+            }
+        }
+    };
+
+    rsx! {
         label {
             input {
                 r#type: "checkbox",
-                checked: "{enable_directory_upload}",
-                oninput: move |evt| {
-                    enable_directory_upload.set(evt.value().parse().unwrap());
-                },
+                checked: enable_directory_upload,
+                oninput: move |evt| enable_directory_upload.set(evt.checked()),
             },
             "Enable directory upload"
         }
@@ -27,41 +44,16 @@ fn App(cx: Scope) -> Element {
             r#type: "file",
             accept: ".txt,.rs",
             multiple: true,
-            directory: **enable_directory_upload,
-            onchange: |evt| {
-                to_owned![files_uploaded];
-                async move {
-                    if let Some(file_engine) = &evt.files() {
-                        let files = file_engine.files();
-                        for file_name in files {
-                            sleep(std::time::Duration::from_secs(1)).await;
-                            files_uploaded.write().push(file_name);
-                        }
-                    }
-                }
-            },
+            directory: enable_directory_upload,
+            onchange: upload_files,
         }
         div {
             width: "100px",
             height: "100px",
             border: "1px solid black",
             prevent_default: "ondrop dragover dragenter",
-            ondrop: move |evt| {
-                to_owned![files_uploaded];
-                async move {
-                    if let Some(file_engine) = &evt.files() {
-                        let files = file_engine.files();
-                        for file_name in &files {
-                            if let Some(file) = file_engine.read_file_to_string(file_name).await{
-                                files_uploaded.write().push(file);
-                            }
-                        }
-                    }
-                }
-            },
-            ondragover: move |event: DragEvent| {
-                event.stop_propagation();
-            },
+            ondrop: handle_file_drop,
+            ondragover: move |event| event.stop_propagation(),
             "Drop files here"
         }
 
@@ -70,5 +62,5 @@ fn App(cx: Scope) -> Element {
                 li { "{file}" }
             }
         }
-    })
+    }
 }

+ 11 - 13
examples/filedragdrop.rs

@@ -1,19 +1,17 @@
+use dioxus::desktop::Config;
 use dioxus::prelude::*;
-use dioxus_desktop::Config;
 
 fn main() {
-    let cfg = Config::new().with_file_drop_handler(|_w, e| {
-        println!("{e:?}");
-        true
-    });
-
-    dioxus_desktop::launch_with_props(app, (), cfg);
+    LaunchBuilder::desktop()
+        .with_cfg(Config::new().with_file_drop_handler(|_w, e| {
+            println!("{e:?}");
+            true
+        }))
+        .launch(app)
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx!(
-        div {
-            h1 { "drag a file here and check your console" }
-        }
-    ))
+fn app() -> Element {
+    rsx!(
+        div { h1 { "drag a file here and check your console" } }
+    )
 }

+ 37 - 43
examples/flat_router.rs

@@ -1,24 +1,11 @@
 use dioxus::prelude::*;
-use dioxus_desktop::{tao::dpi::LogicalSize, Config, WindowBuilder};
-use dioxus_router::prelude::*;
 
 fn main() {
-    env_logger::init();
-
-    let cfg = Config::new().with_window(
-        WindowBuilder::new()
-            .with_inner_size(LogicalSize::new(600, 1000))
-            .with_resizable(false),
-    );
-
-    dioxus_desktop::launch_cfg(App, cfg)
-}
-
-#[component]
-fn App(cx: Scope) -> Element {
-    render! {
-        Router::<Route> {}
-    }
+    launch(|| {
+        rsx! {
+            Router::<Route> {}
+        }
+    })
 }
 
 #[derive(Routable, Clone)]
@@ -27,52 +14,59 @@ enum Route {
     #[layout(Footer)]
         #[route("/")]
         Home {},
+
         #[route("/games")]
         Games {},
+
         #[route("/play")]
         Play {},
+
         #[route("/settings")]
         Settings {},
 }
 
 #[component]
-fn Footer(cx: Scope) -> Element {
-    render! {
-        div {
-            Outlet::<Route> { }
-
-            p {
-                "----"
-            }
-
-            nav {
-                ul {
-                    li { Link { to: Route::Home {}, "Home" } }
-                    li { Link { to: Route::Games {}, "Games" } }
-                    li { Link { to: Route::Play {}, "Play" } }
-                    li { Link { to: Route::Settings {}, "Settings" } }
-                }
-            }
+fn Footer() -> Element {
+    rsx! {
+        Outlet::<Route> {}
+        p { "----" }
+        nav {
+            style { {STYLE} }
+            Link { to: Route::Home {}, class: "nav-btn", "Home" }
+            Link { to: Route::Games {}, class: "nav-btn", "Games" }
+            Link { to: Route::Play {}, class: "nav-btn", "Play" }
+            Link { to: Route::Settings {}, class: "nav-btn", "Settings" }
         }
     }
 }
 
 #[component]
-fn Home(cx: Scope) -> Element {
-    render!("Home")
+fn Home() -> Element {
+    rsx!("Home")
 }
 
 #[component]
-fn Games(cx: Scope) -> Element {
-    render!("Games")
+fn Games() -> Element {
+    rsx!("Games")
 }
 
 #[component]
-fn Play(cx: Scope) -> Element {
-    render!("Play")
+fn Play() -> Element {
+    rsx!("Play")
 }
 
 #[component]
-fn Settings(cx: Scope) -> Element {
-    render!("Settings")
+fn Settings() -> Element {
+    rsx!("Settings")
 }
+
+const STYLE: &str = r#"
+    nav {
+        display: flex;
+        justify-content: space-around;
+    }
+    .nav-btn {
+        text-decoration: none;
+        color: black;
+    }
+"#;

+ 4 - 4
examples/form.rs

@@ -6,11 +6,11 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         div {
             h1 { "Form" }
             form {
@@ -24,5 +24,5 @@ fn app(cx: Scope) -> Element {
                 button { r#type: "submit", value: "Submit", "Submit the form" }
             }
         }
-    })
+    }
 }

+ 0 - 152
examples/framework_benchmark.rs

@@ -1,152 +0,0 @@
-#![allow(non_snake_case)]
-
-use dioxus::prelude::*;
-use rand::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-#[derive(Clone, PartialEq)]
-struct Label {
-    key: usize,
-    labels: [&'static str; 3],
-}
-
-impl Label {
-    fn new_list(num: usize) -> Vec<Self> {
-        let mut rng = SmallRng::from_entropy();
-        let mut labels = Vec::with_capacity(num);
-        for x in 0..num {
-            labels.push(Label {
-                key: x,
-                labels: [
-                    ADJECTIVES.choose(&mut rng).unwrap(),
-                    COLOURS.choose(&mut rng).unwrap(),
-                    NOUNS.choose(&mut rng).unwrap(),
-                ],
-            });
-        }
-        labels
-    }
-}
-
-fn app(cx: Scope) -> Element {
-    let items = use_ref(cx, Vec::new);
-    let selected = use_state(cx, || None);
-
-    cx.render(rsx! {
-        div { class: "container",
-            div { class: "jumbotron",
-                div { class: "row",
-                    div { class: "col-md-6", h1 { "Dioxus" } }
-                    div { class: "col-md-6",
-                        div { class: "row",
-                            ActionButton { name: "Create 1,000 rows", id: "run",
-                                onclick: move |_| items.set(Label::new_list(1_000)),
-                            }
-                            ActionButton { name: "Create 10,000 rows", id: "runlots",
-                                onclick: move |_| items.set(Label::new_list(10_000)),
-                            }
-                            ActionButton { name: "Append 1,000 rows", id: "add",
-                                onclick: move |_| items.write().extend(Label::new_list(1_000)),
-                            }
-                            ActionButton { name: "Update every 10th row", id: "update",
-                                onclick: move |_| items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"),
-                            }
-                            ActionButton { name: "Clear", id: "clear",
-                                onclick: move |_| items.write().clear(),
-                            }
-                            ActionButton { name: "Swap rows", id: "swaprows",
-                                onclick: move |_| items.write().swap(0, 998),
-                            }
-                        }
-                    }
-                }
-            }
-            table {
-                tbody {
-                    for (id, item) in items.read().iter().enumerate() {
-                        tr {
-                            class: if (*selected).map(|s| s == id).unwrap_or(false) { "danger" },
-                            td { class:"col-md-1" }
-                            td { class:"col-md-1", "{item.key}" }
-                            td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
-                                a { class: "lbl", "{item.labels[0]}{item.labels[1]}{item.labels[2]}" }
-                            }
-                            td { class: "col-md-1",
-                                a { class: "remove", onclick: move |_| { items.write().remove(id); },
-                                    span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" }
-                                }
-                            }
-                            td { class: "col-md-6" }
-                        }
-                    }
-
-                }
-             }
-            span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" }
-        }
-    })
-}
-
-#[derive(Props)]
-struct ActionButtonProps<'a> {
-    name: &'a str,
-    id: &'a str,
-    onclick: EventHandler<'a>,
-}
-
-fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
-    cx.render(rsx! {
-        div {
-            class: "col-sm-6 smallpad",
-            button {
-                class:"btn btn-primary btn-block",
-                r#type: "button",
-                id: "{cx.props.id}",
-                onclick: move |_| cx.props.onclick.call(()),
-
-                "{cx.props.name}"
-            }
-        }
-    })
-}
-
-static ADJECTIVES: &[&str] = &[
-    "pretty",
-    "large",
-    "big",
-    "small",
-    "tall",
-    "short",
-    "long",
-    "handsome",
-    "plain",
-    "quaint",
-    "clean",
-    "elegant",
-    "easy",
-    "angry",
-    "crazy",
-    "helpful",
-    "mushy",
-    "odd",
-    "unsightly",
-    "adorable",
-    "important",
-    "inexpensive",
-    "cheap",
-    "expensive",
-    "fancy",
-];
-
-static COLOURS: &[&str] = &[
-    "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
-    "orange",
-];
-
-static NOUNS: &[&str] = &[
-    "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
-    "pizza", "mouse", "keyboard",
-];

+ 8 - 8
examples/generic_component.rs

@@ -3,22 +3,22 @@ use std::fmt::Display;
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    render! {
+fn app() -> Element {
+    rsx! {
         generic_child { data: 0 }
     }
 }
 
-#[derive(PartialEq, Props)]
-struct GenericChildProps<T: Display + PartialEq> {
+#[derive(PartialEq, Props, Clone)]
+struct GenericChildProps<T: Display + PartialEq + Clone + 'static> {
     data: T,
 }
 
-fn generic_child<T: Display + PartialEq>(cx: Scope<GenericChildProps<T>>) -> Element {
-    render! {
-        div { "{&cx.props.data}" }
+fn generic_child<T: Display + PartialEq + Clone>(props: GenericChildProps<T>) -> Element {
+    rsx! {
+        div { "{props.data}" }
     }
 }

+ 20 - 0
examples/global.rs

@@ -0,0 +1,20 @@
+//! Example: README.md showcase
+//!
+//! The example from the README.md.
+
+use dioxus::prelude::*;
+
+fn main() {
+    launch(app);
+}
+
+static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
+static DOUBLED_COUNT: GlobalMemo<i32> = Signal::global_memo(|| COUNT() * 2);
+
+fn app() -> Element {
+    rsx! {
+        h1 { "{COUNT} x 2 = {DOUBLED_COUNT}" }
+        button { onclick: move |_| *COUNT.write() += 1, "Up high!" }
+        button { onclick: move |_| *COUNT.write() -= 1, "Down low!" }
+    }
+}

+ 0 - 28
examples/heavy_compute.rs

@@ -1,28 +0,0 @@
-//! This example shows that you can place heavy work on the main thread, and then
-//!
-//! You *should* be using `tokio::spawn_blocking` instead.
-//!
-//! Your app runs in an async runtime (Tokio), so you should avoid blocking
-//! the rendering of the VirtualDom.
-//!
-//!
-
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    // This is discouraged
-    std::thread::sleep(std::time::Duration::from_millis(2_000));
-
-    // This is suggested
-    tokio::task::spawn_blocking(move || {
-        std::thread::sleep(std::time::Duration::from_millis(2_000));
-    });
-
-    cx.render(rsx! {
-        div { "Hello, world!" }
-    })
-}

+ 3 - 3
examples/hello_world.rs

@@ -1,11 +1,11 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    render! {
+fn app() -> Element {
+    rsx! {
         div { "Hello, world!" }
     }
 }

+ 14 - 13
examples/hydration.rs

@@ -9,27 +9,28 @@
 //! In this example, we pre-render the page to HTML and then pass it into the desktop configuration. This serves as a
 //! proof-of-concept for the hydration feature, but you'll probably only want to use hydration for the web.
 
+use dioxus::desktop::Config;
 use dioxus::prelude::*;
-use dioxus_desktop::Config;
 
 fn main() {
-    let mut vdom = VirtualDom::new(app);
-    let _ = vdom.rebuild();
-    let content = dioxus_ssr::pre_render(&vdom);
+    LaunchBuilder::desktop()
+        .with_cfg(Config::new().with_prerendered({
+            // We build the dom a first time, then pre-render it to HTML
+            let pre_rendered_dom = VirtualDom::prebuilt(app);
 
-    dioxus_desktop::launch_cfg(app, Config::new().with_prerendered(content));
+            // We then launch the app with the pre-rendered HTML
+            dioxus_ssr::pre_render(&pre_rendered_dom)
+        }))
+        .launch(app)
 }
 
-fn app(cx: Scope) -> Element {
-    let val = use_state(cx, || 0);
+fn app() -> Element {
+    let mut val = use_signal(|| 0);
 
-    cx.render(rsx! {
+    rsx! {
         div {
             h1 { "hello world. Count: {val}" }
-            button {
-                onclick: move |_| *val.make_mut() += 1,
-                "click to increment"
-            }
+            button { onclick: move |_| val += 1, "click to increment" }
         }
-    })
+    }
 }

+ 0 - 43
examples/inlineprops.rs

@@ -1,43 +0,0 @@
-//! Run with `cargo-expand` to see what each one expands to.
-//! This file is named `inlineprops.rs`, because there used to be a `#[inline_props]` macro to
-//! do this. However, it's now deprecated (and will likely be removed in a future major version),
-//! so please use `#[component]` instead!
-use dioxus::prelude::*;
-
-#[component]
-fn Thing1<T>(cx: Scope, _a: T) -> Element {
-    cx.render(rsx! { "" })
-}
-
-#[component]
-fn Thing2(cx: Scope, _a: u32) -> Element<'a> {
-    cx.render(rsx! { "" })
-}
-
-#[component]
-fn Thing3<'a, T>(cx: Scope<'a>, _a: &'a T) -> Element<'a> {
-    cx.render(rsx! { "" })
-}
-
-#[component]
-fn Thing4<'a>(cx: Scope<'a>, _a: &'a u32) -> Element<'a> {
-    cx.render(rsx! { "" })
-}
-
-fn main() {
-    dioxus_desktop::launch(App);
-}
-
-#[component]
-fn App(cx: Scope) -> Element {
-    let state = use_state(cx, || 1);
-
-    cx.render(rsx! {
-        div {
-            Thing1 { _a: 1 },
-            Thing2 { _a: 1 },
-            Thing3 { _a: state },
-            Thing4 { _a: state },
-        }
-    })
-}

+ 33 - 39
examples/inputs.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
 const FIELDS: &[(&str, &str)] = &[
@@ -34,23 +34,21 @@ const FIELDS: &[(&str, &str)] = &[
     ("week", ""),  // degrades to text most of the time
 ];
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         div { margin_left: "30px",
-            {select_example(cx)},
+            {select_example()},
             div {
                 // handling inputs on divs will catch all input events below
                 // so the value of our input event will be either huey, dewey, louie, or true/false (because of the checkboxe)
                 // be mindful in grouping inputs together, as they will all be handled by the same event handler
-                oninput: move |evt| {
-                    println!("{evt:?}");
-                },
+                oninput: move |evt| println!("{evt:?}"),
                 div {
                     input {
                         id: "huey",
                         r#type: "radio",
                         value: "huey",
-                        checked: "",
+                        checked: true,
                         name: "drone",
                     }
                     label {
@@ -65,10 +63,7 @@ fn app(cx: Scope) -> Element {
                         value: "dewey",
                         name: "drone",
                     }
-                    label {
-                        r#for: "dewey",
-                        "Dewey"
-                    }
+                    label { r#for: "dewey", "Dewey" }
                 }
                 div {
                     input {
@@ -133,36 +128,35 @@ fn app(cx: Scope) -> Element {
                 }
             }
         }
-    })
+    }
 }
 
-fn select_example(cx: Scope) -> Element {
-    cx.render(rsx! {
-    div {
-        select {
-            id: "selection",
-            name: "selection",
-            multiple: true,
-            oninput: move |evt| {
-                println!("{evt:?}");
-            },
-            option {
-                value : "Option 1",
-                label : "Option 1",
+fn select_example() -> Element {
+    rsx! {
+        div {
+            select {
+                id: "selection",
+                name: "selection",
+                multiple: true,
+                oninput: move |evt| println!("{evt:?}"),
+                option {
+                    value: "Option 1",
+                    label: "Option 1",
+                }
+                option {
+                    value: "Option 2",
+                    label: "Option 2",
+                    selected: true,
+                },
+                option {
+                    value: "Option 3",
+                    label: "Option 3",
+                }
             }
-            option {
-                value : "Option 2",
-                label : "Option 2",
-                selected : true,
-            },
-            option {
-                value : "Option 3",
-                label : "Option 3",
+            label {
+                r#for: "selection",
+                "select element"
             }
         }
-        label {
-            r#for: "selection",
-            "select element"
-        }
-    }})
+    }
 }

+ 19 - 20
examples/link.rs

@@ -1,30 +1,25 @@
 use dioxus::prelude::*;
-use dioxus_router::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(App);
+    launch_desktop(App);
 }
 
 #[component]
-fn App(cx: Scope) -> Element {
-    cx.render(rsx! (
+fn App() -> Element {
+    rsx! (
         div {
-            p {
-                a { href: "http://dioxuslabs.com/", "Default link - links outside of your app" }
-            }
+            p { a { href: "http://dioxuslabs.com/", "Default link - links outside of your app" } }
             p {
                 a {
                     href: "http://dioxuslabs.com/",
                     prevent_default: "onclick",
                     onclick: |_| println!("Hello Dioxus"),
-                    "Custom event link - links inside of your app",
+                    "Custom event link - links inside of your app"
                 }
             }
         }
-        div {
-            Router::<Route> {}
-        }
-    ))
+        div { Router::<Route> {} }
+    )
 }
 
 #[derive(Routable, Clone)]
@@ -38,23 +33,27 @@ enum Route {
 }
 
 #[component]
-fn Header(cx: Scope) -> Element {
-    render! {
+fn Header() -> Element {
+    rsx! {
         h1 { "Your app here" }
         ul {
-            li { Link { to: Route::Home {}, "home" } }
-            li { Link { to: Route::Settings {}, "settings" } }
+            li {
+                Link { to: Route::Home {}, "home" }
+            }
+            li {
+                Link { to: Route::Settings {}, "settings" }
+            }
         }
         Outlet::<Route> {}
     }
 }
 
 #[component]
-fn Home(cx: Scope) -> Element {
-    render!(h1 { "Home" })
+fn Home() -> Element {
+    rsx!( h1 { "Home" } )
 }
 
 #[component]
-fn Settings(cx: Scope) -> Element {
-    render!(h1 { "Settings" })
+fn Settings() -> Element {
+    rsx!( h1 { "Settings" } )
 }

+ 5 - 5
examples/login_form.rs

@@ -4,10 +4,10 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
+fn app() -> Element {
     let onsubmit = move |evt: FormEvent| async move {
         let resp = reqwest::Client::new()
             .post("http://localhost:8080/login")
@@ -29,9 +29,9 @@ fn app(cx: Scope) -> Element {
         }
     };
 
-    cx.render(rsx! {
+    rsx! {
         h1 { "Login" }
-        form { onsubmit: onsubmit,
+        form { onsubmit,
             input { r#type: "text", id: "username", name: "username" }
             label { "Username" }
             br {}
@@ -40,5 +40,5 @@ fn app(cx: Scope) -> Element {
             br {}
             button { "Login" }
         }
-    })
+    }
 }

+ 44 - 0
examples/memo_chain.rs

@@ -0,0 +1,44 @@
+use dioxus::prelude::*;
+
+fn main() {
+    launch_desktop(app);
+}
+
+fn app() -> Element {
+    let mut value = use_signal(|| 0);
+    let mut depth = use_signal(|| 0_usize);
+    let items = use_memo(move || (0..depth()).map(|f| f as _).collect::<Vec<isize>>());
+    let state = use_memo(move || value() + 1);
+
+    println!("rendering app");
+
+    rsx! {
+        button { onclick: move |_| value += 1, "Increment" }
+        button { onclick: move |_| depth += 1, "Add depth" }
+        button { onclick: move |_| depth -= 1, "Remove depth" }
+        Child { depth, items, state }
+    }
+}
+
+#[component]
+fn Child(
+    state: ReadOnlySignal<isize>,
+    items: ReadOnlySignal<Vec<isize>>,
+    depth: ReadOnlySignal<usize>,
+) -> Element {
+    if depth() == 0 {
+        return None;
+    }
+
+    // These memos don't get re-computed when early returns happen
+    let state = use_memo(move || state() + 1);
+    let item = use_memo(move || items()[depth()]);
+    let depth = use_memo(move || depth() - 1);
+
+    println!("rendering child: {}", depth());
+
+    rsx! {
+        h3 { "Depth({depth})-Item({item}): {state}"}
+        Child { depth, state, items }
+    }
+}

+ 1 - 1
examples/mobile_demo/Cargo.toml

@@ -2,7 +2,7 @@
 name = "mobile-demo"
 version = "0.1.0"
 authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
-edition = "2018"
+edition = "2021"
 
 [lib]
 crate-type = ["staticlib", "cdylib", "rlib"]

+ 12 - 8
examples/mobile_demo/src/lib.rs

@@ -1,6 +1,6 @@
 use anyhow::Result;
+use dioxus::desktop::Config;
 use dioxus::prelude::*;
-use dioxus_desktop::Config;
 #[cfg(target_os = "android")]
 use wry::android_binding;
 
@@ -49,8 +49,7 @@ pub fn main() -> Result<()> {
 
     // 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_desktop::launch_cfg(
-        app,
+    LaunchBuilder::new().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()),
@@ -59,17 +58,22 @@ pub fn main() -> Result<()> {
     Ok(())
 }
 
-fn app(cx: Scope) -> Element {
+fn app() -> Element {
     let items = cx.use_hook(|| vec![1, 2, 3]);
 
     log::debug!("Hello from the app");
 
-    render! {
+    rsx! {
         div {
-            h1 { "Hello, Mobile"}
-            div { margin_left: "auto", margin_right: "auto", width: "200px", padding: "10px", border: "1px solid black",
+            h1 { "Hello, Mobile" }
+            div {
+                margin_left: "auto",
+                margin_right: "auto",
+                width: "200px",
+                padding: "10px",
+                border: "1px solid black",
                 button {
-                    onclick: move|_| {
+                    onclick: move |_| {
                         println!("Clicked!");
                         items.push(items.len());
                         cx.needs_update_any(ScopeId::ROOT);

+ 14 - 17
examples/multiwindow.rs

@@ -1,25 +1,22 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
-        div {
-            button {
-                onclick: move |_| {
-                    let dom = VirtualDom::new(popup);
-                    dioxus_desktop::window().new_window(dom, Default::default());
-                },
-                "New Window"
-            }
-        }
-    })
+fn app() -> Element {
+    let onclick = move |_| {
+        let dom = VirtualDom::new(popup);
+        dioxus::desktop::window().new_window(dom, Default::default());
+    };
+
+    rsx! {
+        button { onclick, "New Window" }
+    }
 }
 
-fn popup(cx: Scope) -> Element {
-    cx.render(rsx! {
-        div { "This is a popup!" }
-    })
+fn popup() -> Element {
+    rsx! {
+        div { "This is a popup window!" }
+    }
 }

+ 4 - 4
examples/nested_listeners.rs

@@ -7,11 +7,11 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         div {
             onclick: move |_| println!("clicked! top"),
             "- div"
@@ -30,5 +30,5 @@ fn app(cx: Scope) -> Element {
                 "Does not handle clicks - only propagate"
             }
         }
-    })
+    }
 }

+ 4 - 6
examples/openid_connect_demo/src/main.rs

@@ -14,8 +14,6 @@ pub(crate) mod views;
 use oidc::{AuthRequestState, AuthTokenState};
 use router::Route;
 
-use dioxus_router::prelude::*;
-
 use crate::{
     constants::{DIOXUS_FRONT_AUTH_REQUEST, DIOXUS_FRONT_AUTH_TOKEN},
     oidc::ClientState,
@@ -30,14 +28,14 @@ pub static DIOXUS_FRONT_ISSUER_URL: &str = env!("DIOXUS_FRONT_ISSUER_URL");
 pub static DIOXUS_FRONT_CLIENT_ID: &str = env!("DIOXUS_FRONT_CLIENT_ID");
 pub static DIOXUS_FRONT_URL: &str = env!("DIOXUS_FRONT_URL");
 
-fn App(cx: Scope) -> Element {
+fn App() -> Element {
     use_init_atom_root(cx);
 
     // Retrieve the value stored in the browser's storage
     let stored_auth_token = LocalStorage::get(DIOXUS_FRONT_AUTH_TOKEN)
         .ok()
         .unwrap_or(AuthTokenState::default());
-    let fermi_auth_token = use_atom_ref(cx, &FERMI_AUTH_TOKEN);
+    let fermi_auth_token = use_atom_ref(&FERMI_AUTH_TOKEN);
     if fermi_auth_token.read().is_none() {
         *fermi_auth_token.write() = Some(stored_auth_token);
     }
@@ -45,11 +43,11 @@ fn App(cx: Scope) -> Element {
     let stored_auth_request = LocalStorage::get(DIOXUS_FRONT_AUTH_REQUEST)
         .ok()
         .unwrap_or(AuthRequestState::default());
-    let fermi_auth_request = use_atom_ref(cx, &FERMI_AUTH_REQUEST);
+    let fermi_auth_request = use_atom_ref(&FERMI_AUTH_REQUEST);
     if fermi_auth_request.read().is_none() {
         *fermi_auth_request.write() = Some(stored_auth_request);
     }
-    render! { Router::<Route> {} }
+    rsx! { Router::<Route> {} }
 }
 
 fn main() {

+ 0 - 1
examples/openid_connect_demo/src/router.rs

@@ -1,6 +1,5 @@
 use crate::views::{header::AuthHeader, home::Home, login::Login, not_found::NotFound};
 use dioxus::prelude::*;
-use dioxus_router::prelude::*;
 
 #[derive(Routable, Clone)]
 pub enum Route {

+ 15 - 17
examples/openid_connect_demo/src/views/header.rs

@@ -9,15 +9,15 @@ use crate::{
     FERMI_AUTH_REQUEST, FERMI_AUTH_TOKEN, FERMI_CLIENT,
 };
 use dioxus::prelude::*;
-use dioxus_router::prelude::{Link, Outlet};
+use dioxus::router::prelude::{Link, Outlet};
 use fermi::*;
 use openidconnect::{url::Url, OAuth2TokenResponse, TokenResponse};
 
 #[component]
 pub fn LogOut(cx: Scope<ClientProps>) -> Element {
-    let fermi_auth_token = use_atom_ref(cx, &FERMI_AUTH_TOKEN);
+    let fermi_auth_token = use_atom_ref(&FERMI_AUTH_TOKEN);
     let fermi_auth_token_read = fermi_auth_token.read().clone();
-    let log_out_url_state = use_state(cx, || None::<Option<Result<Url, crate::errors::Error>>>);
+    let log_out_url_state = use_signal(|| None::<Option<Result<Url, crate::errors::Error>>>);
     cx.render(match fermi_auth_token_read {
         Some(fermi_auth_token_read) => match fermi_auth_token_read.id_token.clone() {
             Some(id_token) => match log_out_url_state.get() {
@@ -40,9 +40,7 @@ pub fn LogOut(cx: Scope<ClientProps>) -> Element {
                             }
                         }
                         Err(error) => {
-                            rsx! {
-                                div { "Failed to load disconnection url: {error:?}" }
-                            }
+                            rsx! { div { "Failed to load disconnection url: {error:?}" } }
                         }
                     },
                     None => {
@@ -61,7 +59,7 @@ pub fn LogOut(cx: Scope<ClientProps>) -> Element {
                         })
                     };
                     logout_url_task();
-                    rsx! { div{"Loading log out url... Please wait"}}
+                    rsx! { div { "Loading log out url... Please wait" } }
                 }
             },
             None => {
@@ -76,8 +74,8 @@ pub fn LogOut(cx: Scope<ClientProps>) -> Element {
 
 #[component]
 pub fn RefreshToken(cx: Scope<ClientProps>) -> Element {
-    let fermi_auth_token = use_atom_ref(cx, &FERMI_AUTH_TOKEN);
-    let fermi_auth_request = use_atom_ref(cx, &FERMI_AUTH_REQUEST);
+    let fermi_auth_token = use_atom_ref(&FERMI_AUTH_TOKEN);
+    let fermi_auth_request = use_atom_ref(&FERMI_AUTH_REQUEST);
     let fermi_auth_token_read = fermi_auth_token.read().clone();
     cx.render(match fermi_auth_token_read {
         Some(fermi_auth_client_read) => match fermi_auth_client_read.refresh_token {
@@ -128,9 +126,9 @@ pub fn RefreshToken(cx: Scope<ClientProps>) -> Element {
 }
 
 #[component]
-pub fn LoadClient(cx: Scope) -> Element {
-    let init_client_future = use_future(cx, (), |_| async move { init_oidc_client().await });
-    let fermi_client: &UseAtomRef<ClientState> = use_atom_ref(cx, &FERMI_CLIENT);
+pub fn LoadClient() -> Element {
+    let init_client_future = use_future(move || async move { init_oidc_client().await });
+    let fermi_client: &UseAtomRef<ClientState> = use_atom_ref(&FERMI_CLIENT);
     cx.render(match init_client_future.value() {
         Some(client_props) => match client_props {
             Ok((client_id, client)) => {
@@ -162,10 +160,10 @@ pub fn LoadClient(cx: Scope) -> Element {
 }
 
 #[component]
-pub fn AuthHeader(cx: Scope) -> Element {
-    let auth_token = use_atom_ref(cx, &FERMI_AUTH_TOKEN);
-    let fermi_auth_request = use_atom_ref(cx, &FERMI_AUTH_REQUEST);
-    let fermi_client: &UseAtomRef<ClientState> = use_atom_ref(cx, &FERMI_CLIENT);
+pub fn AuthHeader() -> Element {
+    let auth_token = use_atom_ref(&FERMI_AUTH_TOKEN);
+    let fermi_auth_request = use_atom_ref(&FERMI_AUTH_REQUEST);
+    let fermi_client: &UseAtomRef<ClientState> = use_atom_ref(&FERMI_CLIENT);
     let client = fermi_client.read().oidc_client.clone();
     let auth_request_read = fermi_auth_request.read().clone();
     let auth_token_read = auth_token.read().clone();
@@ -197,7 +195,7 @@ pub fn AuthHeader(cx: Scope) -> Element {
                                         log::info!("Token expired");
                                         rsx! {
                                             div {
-                                                RefreshToken {client_id: client_props.client_id, client: client_props.client}
+                                                RefreshToken { client_id: client_props.client_id, client: client_props.client }
                                                 Outlet::<Route> {}
                                             }
                                         }

+ 2 - 2
examples/openid_connect_demo/src/views/home.rs

@@ -1,5 +1,5 @@
 use dioxus::prelude::*;
 
-pub fn Home(cx: Scope) -> Element {
-    render! { div { "Hello world" } }
+pub fn Home() -> Element {
+    rsx! { div { "Hello world" } }
 }

+ 5 - 5
examples/openid_connect_demo/src/views/login.rs

@@ -5,16 +5,16 @@ use crate::{
     DIOXUS_FRONT_URL, FERMI_AUTH_REQUEST, FERMI_AUTH_TOKEN, FERMI_CLIENT,
 };
 use dioxus::prelude::*;
-use dioxus_router::prelude::{Link, NavigationTarget};
+use dioxus::router::prelude::{Link, NavigationTarget};
 use fermi::*;
 use openidconnect::{OAuth2TokenResponse, TokenResponse};
 
 #[component]
-pub fn Login(cx: Scope, query_string: String) -> Element {
-    let fermi_client = use_atom_ref(cx, &FERMI_CLIENT);
-    let fermi_auth_token = use_atom_ref(cx, &FERMI_AUTH_TOKEN);
+pub fn Login(query_string: String) -> Element {
+    let fermi_client = use_atom_ref(&FERMI_CLIENT);
+    let fermi_auth_token = use_atom_ref(&FERMI_AUTH_TOKEN);
     let home_url: NavigationTarget<Route> = DIOXUS_FRONT_URL.parse().unwrap();
-    let fermi_auth_request = use_atom_ref(cx, &FERMI_AUTH_REQUEST);
+    let fermi_auth_request = use_atom_ref(&FERMI_AUTH_REQUEST);
     let client = fermi_client.read().oidc_client.clone();
     let auth_token_read = fermi_auth_token.read().clone();
     cx.render(match (client, auth_token_read) {

+ 2 - 2
examples/openid_connect_demo/src/views/not_found.rs

@@ -1,8 +1,8 @@
 use dioxus::prelude::*;
 
 #[component]
-pub fn NotFound(cx: Scope, route: Vec<String>) -> Element {
-    render! {
+pub fn NotFound(route: Vec<String>) -> Element {
+    rsx! {
         div{
             {route.join("")}
         }

+ 15 - 15
examples/optional_props.rs

@@ -7,11 +7,11 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         Button {
             a: "asd".to_string(),
             c: "asd".to_string(),
@@ -30,12 +30,10 @@ fn app(cx: Scope) -> Element {
             c: "asd".to_string(),
             d: Some("asd".to_string()),
         }
-    })
+    }
 }
 
-type SthElse<T> = Option<T>;
-
-#[derive(Props, PartialEq)]
+#[derive(Props, PartialEq, Clone)]
 struct ButtonProps {
     a: String,
 
@@ -51,14 +49,16 @@ struct ButtonProps {
     e: SthElse<String>,
 }
 
-fn Button(cx: Scope<ButtonProps>) -> Element {
-    cx.render(rsx! {
+type SthElse<T> = Option<T>;
+
+fn Button(props: ButtonProps) -> Element {
+    rsx! {
         button {
-            "{cx.props.a} | "
-            "{cx.props.b:?} | "
-            "{cx.props.c:?} | "
-            "{cx.props.d:?} | "
-            "{cx.props.e:?}"
+            "{props.a} | "
+            "{props.b:?} | "
+            "{props.c:?} | "
+            "{props.d:?} | "
+            "{props.e:?}"
         }
-    })
+    }
 }

+ 8 - 8
examples/overlay.rs

@@ -1,12 +1,12 @@
+use dioxus::desktop::{tao::dpi::PhysicalPosition, LogicalSize, WindowBuilder};
 use dioxus::prelude::*;
-use dioxus_desktop::{tao::dpi::PhysicalPosition, LogicalSize, WindowBuilder};
 
 fn main() {
-    dioxus_desktop::launch_cfg(app, make_config());
+    LaunchBuilder::desktop().with_cfg(make_config()).launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         div {
             width: "100%",
             height: "100%",
@@ -17,16 +17,16 @@ fn app(cx: Scope) -> Element {
                 width: "100%",
                 height: "10px",
                 background_color: "black",
-                onmousedown: move |_| dioxus_desktop::window().drag(),
+                onmousedown: move |_| dioxus::desktop::window().drag(),
             }
 
             "This is an overlay!"
         }
-    })
+    }
 }
 
-fn make_config() -> dioxus_desktop::Config {
-    dioxus_desktop::Config::default()
+fn make_config() -> dioxus::desktop::Config {
+    dioxus::desktop::Config::default()
         .with_window(make_window())
         .with_custom_head(
             r#"

+ 14 - 17
examples/query_segments_demo/src/main.rs → examples/query_segments.rs

@@ -1,16 +1,12 @@
-#![allow(unused)]
 //! Example: Url query segments usage
 //! ------------------------------------
 //!
 //! This example shows how to access and use multiple query segments present in an url on the web.
 //!
 //! Run `dx serve` and navigate to `http://localhost:8080/blog?name=John&surname=Doe`
-use std::fmt::Display;
-
 use dioxus::prelude::*;
-use dioxus_router::prelude::*;
+use std::fmt::Display;
 
-// ANCHOR: route
 #[derive(Routable, Clone)]
 #[rustfmt::skip]
 enum Route {
@@ -20,6 +16,7 @@ enum Route {
         // You must include query segments in child variants
         query_params: ManualBlogQuerySegments,
     },
+
     // segments that follow the ?:field&:other_field syntax are query segments that follow the standard url query syntax
     #[route("/autoblog?:name&:surname")]
     AutomaticBlogPost {
@@ -41,7 +38,7 @@ impl Display for ManualBlogQuerySegments {
     }
 }
 
-/// The query segment is anything that implements <https://docs.rs/dioxus-router/latest/dioxus_router/routable/trait.FromQuery.html>. You can implement that trait for a struct if you want to parse multiple query parameters.
+/// The query segment is anything that implements <https://docs.rs/dioxus-router/latest/dioxus::router/routable/trait.FromQuery.html>. You can implement that trait for a struct if you want to parse multiple query parameters.
 impl FromQuery for ManualBlogQuerySegments {
     fn from_query(query: &str) -> Self {
         let mut name = None;
@@ -63,26 +60,26 @@ impl FromQuery for ManualBlogQuerySegments {
 }
 
 #[component]
-fn BlogPost(cx: Scope, query_params: ManualBlogQuerySegments) -> Element {
-    render! {
-        div{"This is your blogpost with a query segment:"}
-        div{ "{query_params:?}" }
+fn BlogPost(query_params: ManualBlogQuerySegments) -> Element {
+    rsx! {
+        div { "This is your blogpost with a query segment:" }
+        div { "{query_params:?}" }
     }
 }
 
 #[component]
-fn AutomaticBlogPost(cx: Scope, name: String, surname: String) -> Element {
-    render! {
-        div{"This is your blogpost with a query segment:"}
-        div{ "name={name}&surname={surname}" }
+fn AutomaticBlogPost(name: String, surname: String) -> Element {
+    rsx! {
+        div { "This is your blogpost with a query segment:" }
+        div { "name={name}&surname={surname}" }
     }
 }
 
 #[component]
-fn App(cx: Scope) -> Element {
-    render! { Router::<Route>{} }
+fn App() -> Element {
+    rsx! { Router::<Route> {} }
 }
 
 fn main() {
-    dioxus_web::launch(App);
+    launch(App);
 }

+ 0 - 13
examples/query_segments_demo/Cargo.toml

@@ -1,13 +0,0 @@
-[package]
-name = "query_segments_demo"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-dioxus = { path = "../../packages/dioxus", version = "*" }
-dioxus-router = { path = "../../packages/router", version = "*" }
-dioxus-web = { path = "../../packages/web", version = "*" }
-form_urlencoded = "1.2.0"

+ 25 - 31
examples/read_size.rs

@@ -4,10 +4,10 @@ use std::rc::Rc;
 use dioxus::{html::geometry::euclid::Rect, prelude::*};
 
 fn main() {
-    dioxus_desktop::launch_cfg(
-        app,
-        dioxus_desktop::Config::default().with_custom_head(
-            r#"
+    LaunchBuilder::desktop()
+        .with_cfg(
+            dioxus::desktop::Config::default().with_custom_head(
+                r#"
 <style type="text/css">
     html, body {
         height: 100%;
@@ -20,41 +20,35 @@ fn main() {
     }
 </style>
 "#
-            .to_owned(),
-        ),
-    );
+                .to_owned(),
+            ),
+        )
+        .launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let div_element: &UseRef<Option<Rc<MountedData>>> = use_ref(cx, || None);
+fn app() -> Element {
+    let mut div_element = use_signal(|| None as Option<Rc<MountedData>>);
+    let mut dimensions = use_signal(Rect::zero);
 
-    let dimentions = use_ref(cx, Rect::zero);
+    let read_dims = move |_| async move {
+        let read = div_element.read();
+        let client_rect = read.as_ref().map(|el| el.get_client_rect());
+        if let Some(client_rect) = client_rect {
+            if let Ok(rect) = client_rect.await {
+                dimensions.set(rect);
+            }
+        }
+    };
 
-    cx.render(rsx!(
+    rsx!(
         div {
             width: "50%",
             height: "50%",
             background_color: "red",
-            onmounted: move |cx| {
-                div_element.set(Some(cx.inner().clone()));
-            },
-            "This element is {dimentions.read():?}"
+            onmounted: move |cx| div_element.set(Some(cx.data())),
+            "This element is {dimensions():?}"
         }
 
-        button {
-            onclick: move |_| {
-                to_owned![div_element, dimentions];
-                async move {
-                    let read = div_element.read();
-                    let client_rect = read.as_ref().map(|el| el.get_client_rect());
-                    if let Some(client_rect) = client_rect {
-                        if let Ok(rect) = client_rect.await {
-                            dimentions.set(rect);
-                        }
-                    }
-                }
-            },
-            "Read dimentions"
-        }
-    ))
+        button { onclick: read_dims, "Read dimensions" }
+    )
 }

+ 5 - 9
examples/readme.rs

@@ -1,19 +1,15 @@
-//! Example: README.md showcase
-//!
-//! The example from the README.md.
-
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let mut count = use_state(cx, || 0);
+fn app() -> Element {
+    let mut count = use_signal(|| 0);
 
-    cx.render(rsx! {
+    rsx! {
         h1 { "High-Five counter: {count}" }
         button { onclick: move |_| count += 1, "Up high!" }
         button { onclick: move |_| count -= 1, "Down low!" }
-    })
+    }
 }

+ 8 - 18
examples/pattern_reducer.rs → examples/reducer.rs

@@ -4,31 +4,24 @@
 //! This example shows how to encapsulate state in dioxus components with the reducer pattern.
 //! This pattern is very useful when a single component can handle many types of input that can
 //! be represented by an enum.
-//!
-//! Currently we don't have a reducer pattern hook. If you'd like to add it,
-//! feel free to make a PR.
 
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let state = use_state(cx, PlayerState::new);
+fn app() -> Element {
+    let mut state = use_signal(|| PlayerState { is_playing: false });
 
-    cx.render(rsx!(
+    rsx!(
         div {
             h1 {"Select an option"}
-            h3 { "The radio is... ", {state.is_playing()}, "!" }
-            button { onclick: move |_| state.make_mut().reduce(PlayerAction::Pause),
-                "Pause"
-            }
-            button { onclick: move |_| state.make_mut().reduce(PlayerAction::Play),
-                "Play"
-            }
+            h3 { "The radio is... ", {state.read().is_playing()}, "!" }
+            button { onclick: move |_| state.write().reduce(PlayerAction::Pause), "Pause" }
+            button { onclick: move |_| state.write().reduce(PlayerAction::Play), "Play" }
         }
-    ))
+    )
 }
 
 enum PlayerAction {
@@ -42,9 +35,6 @@ struct PlayerState {
 }
 
 impl PlayerState {
-    fn new() -> Self {
-        Self { is_playing: false }
-    }
     fn reduce(&mut self, action: PlayerAction) {
         match action {
             PlayerAction::Pause => self.is_playing = false,

+ 32 - 40
examples/router.rs

@@ -1,15 +1,14 @@
 use dioxus::prelude::*;
-use dioxus_router::prelude::*;
 
 fn main() {
-    #[cfg(target_arch = "wasm32")]
-    dioxus_web::launch(App);
-    #[cfg(not(target_arch = "wasm32"))]
-    dioxus_desktop::launch(App);
+    launch_desktop(|| {
+        rsx! {
+            Router::<Route> {}
+        }
+    });
 }
 
-// ANCHOR: router
-#[derive(Routable, Clone)]
+#[derive(Routable, Clone, Debug, PartialEq)]
 #[rustfmt::skip]
 enum Route {
     #[layout(NavBar)]
@@ -19,7 +18,7 @@ enum Route {
             #[layout(Blog)]
                 #[route("/")]
                 BlogList {},
-                #[route("/blog/:name")]
+                #[route("/:name")]
                 BlogPost { name: String },
             #[end_layout]
         #[end_nest]
@@ -33,22 +32,18 @@ enum Route {
         route: Vec<String>,
     },
 }
-// ANCHOR_END: router
-
-#[component]
-fn App(cx: Scope) -> Element {
-    render! {
-        Router::<Route> {}
-    }
-}
 
 #[component]
-fn NavBar(cx: Scope) -> Element {
-    render! {
+fn NavBar() -> Element {
+    rsx! {
         nav {
             ul {
-                li { Link { to: Route::Home {}, "Home" } }
-                li { Link { to: Route::BlogList {}, "Blog" } }
+                li {
+                    Link { to: Route::Home {}, "Home" }
+                }
+                li {
+                    Link { to: Route::BlogList {}, "Blog" }
+                }
             }
         }
         Outlet::<Route> {}
@@ -56,34 +51,36 @@ fn NavBar(cx: Scope) -> Element {
 }
 
 #[component]
-fn Home(cx: Scope) -> Element {
-    render! {
-        h1 { "Welcome to the Dioxus Blog!" }
-    }
+fn Home() -> Element {
+    rsx! { h1 { "Welcome to the Dioxus Blog!" } }
 }
 
 #[component]
-fn Blog(cx: Scope) -> Element {
-    render! {
+fn Blog() -> Element {
+    rsx! {
         h1 { "Blog" }
         Outlet::<Route> {}
     }
 }
 
 #[component]
-fn BlogList(cx: Scope) -> Element {
-    render! {
+fn BlogList() -> Element {
+    rsx! {
         h2 { "Choose a post" }
         ul {
             li {
                 Link {
-                    to: Route::BlogPost { name: "Blog post 1".into() },
+                    to: Route::BlogPost {
+                        name: "Blog post 1".into(),
+                    },
                     "Read the first blog post"
                 }
             }
             li {
                 Link {
-                    to: Route::BlogPost { name: "Blog post 2".into() },
+                    to: Route::BlogPost {
+                        name: "Blog post 2".into(),
+                    },
                     "Read the second blog post"
                 }
             }
@@ -92,20 +89,15 @@ fn BlogList(cx: Scope) -> Element {
 }
 
 #[component]
-fn BlogPost(cx: Scope, name: String) -> Element {
-    render! {
-        h2 { "Blog Post: {name}"}
-    }
+fn BlogPost(name: String) -> Element {
+    rsx! { h2 { "Blog Post: {name}" } }
 }
 
 #[component]
-fn PageNotFound(cx: Scope, route: Vec<String>) -> Element {
-    render! {
+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:\nattemped to navigate to: {route:?}"
-        }
+        pre { color: "red", "log:\nattemped to navigate to: {route:?}" }
     }
 }

+ 0 - 58
examples/rsx_compile_fail.rs

@@ -1,58 +0,0 @@
-//! This example just flexes the ability to use arbitrary expressions within RSX.
-//! It also proves that lifetimes work properly, especially when used with use_ref
-
-use dioxus::prelude::*;
-
-fn main() {
-    let mut vdom = VirtualDom::new(example);
-    _ = vdom.rebuild();
-
-    let mut renderer = dioxus_ssr::Renderer::new();
-    renderer.pretty = true;
-    renderer.render(&vdom);
-}
-
-fn example(cx: Scope) -> Element {
-    let items = use_state(cx, || {
-        vec![Thing {
-            a: "asd".to_string(),
-            b: 10,
-        }]
-    });
-
-    let things = use_ref(cx, || {
-        vec![Thing {
-            a: "asd".to_string(),
-            b: 10,
-        }]
-    });
-    let things_list = things.read();
-
-    let mything = use_ref(cx, || Some(String::from("asd")));
-    let mything_read = mything.read();
-
-    cx.render(rsx!(
-        div {
-            div { id: "asd",
-                "your neighborhood spiderman"
-
-                for item in items.iter().cycle().take(5) {
-                    div { "{item.a}" }
-                }
-
-                for thing in things_list.iter() {
-                    div { "{thing.a}" "{thing.b}" }
-                }
-
-                if let Some(f) = mything_read.as_ref() {
-                    div { "{f}" }
-                }
-            }
-        }
-    ))
-}
-
-struct Thing {
-    a: String,
-    b: u32,
-}

+ 257 - 263
examples/rsx_usage.rs

@@ -39,269 +39,263 @@
 //! - Allow top-level fragments
 
 fn main() {
-    dioxus_desktop::launch(App);
-}
-
-use core::{fmt, str::FromStr};
-use std::fmt::Display;
-
-use baller::Baller;
-use dioxus::prelude::*;
-
-#[component]
-fn App(cx: Scope) -> Element {
-    let formatting = "formatting!";
-    let formatting_tuple = ("a", "b");
-    let lazy_fmt = format_args!("lazily formatted text");
-    let asd = 123;
-    cx.render(rsx! {
-        div {
-            // Elements
-            div {}
-            h1 {"Some text"}
-            h1 {"Some text with {formatting}"}
-            h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
-            h1 {"Formatting without interpolation " {formatting_tuple.0} "and" {formatting_tuple.1} }
-            h2 {
-                "Multiple"
-                "Text"
-                "Blocks"
-                "Use comments as separators in html"
-            }
-            div {
-                h1 {"multiple"}
-                h2 {"nested"}
-                h3 {"elements"}
-            }
-            div {
-                class: "my special div",
-                h1 {"Headers and attributes!"}
-            }
-            div {
-                // pass simple rust expressions in
-                class: lazy_fmt,
-                id: format_args!("attributes can be passed lazily with std::fmt::Arguments"),
-                class: "asd",
-                class: "{asd}",
-                // if statements can be used to conditionally render attributes
-                class: if formatting.contains("form") { "{asd}" },
-                div {
-                    class: {
-                        const WORD: &str = "expressions";
-                        format_args!("Arguments can be passed in through curly braces for complex {WORD}")
-                    }
-                }
-            }
-
-            // Expressions can be used in element position too:
-            {rsx!(p { "More templating!" })},
-
-            // Iterators
-            {(0..10).map(|i| rsx!(li { "{i}" }))},
-
-            // Iterators within expressions
-            {
-                let data = std::collections::HashMap::<&'static str, &'static str>::new();
-                // Iterators *should* have keys when you can provide them.
-                // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
-                // Using an "ID" associated with your data is a good idea.
-                data.into_iter().map(|(k, v)| rsx!(li { key: "{k}", "{v}" }))
-            }
-
-            // Matching
-            match true {
-                true => rsx!( h1 {"Top text"}),
-                false => rsx!( h1 {"Bottom text"})
-            }
-
-            // Conditional rendering
-            // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
-            // You can convert a bool condition to rsx! with .then and .or
-            {true.then(|| rsx!(div {}))},
-
-            // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element
-            if false {
-                h1 {"Top text"}
-            } else {
-                h1 {"Bottom text"}
-            }
-
-            // Using optionals for diverging branches
-            // Note that since this is wrapped in curlies, it's interpreted as an expression
-            {if true {
-                Some(rsx!(h1 {"Top text"}))
-            } else {
-                None
-            }}
-
-            // returning "None" without a diverging branch is a bit noisy... but rare in practice
-            {None as Option<()>},
-
-            // can also just use empty fragments
-            Fragment {}
-
-            // Fragments let you insert groups of nodes without a parent.
-            // This lets you make components that insert elements as siblings without a container.
-            div {"A"}
-            Fragment {
-                div {"B"}
-                div {"C"}
-                Fragment {
-                    "D"
-                    Fragment {
-                        "E"
-                        "F"
-                    }
-                }
-            }
-
-            // Components
-            // Can accept any paths
-            // Notice how you still get syntax highlighting and IDE support :)
-            Baller {}
-            baller::Baller {}
-            crate::baller::Baller {}
-
-            // Can take properties
-            Taller { a: "asd" }
-
-            // Can take optional properties
-            Taller { a: "asd" }
-
-            // Can pass in props directly as an expression
-            {
-                let props = TallerProps {a: "hello", children: None };
-                rsx!(Taller { ..props })
-            }
-
-            // Spreading can also be overridden manually
-            Taller {
-                ..TallerProps { a: "ballin!", children: None },
-                a: "not ballin!"
-            }
-
-            // Can take children too!
-            Taller { a: "asd", div {"hello world!"} }
-
-            // This component's props are defined *inline* with the `inline_props` macro
-            WithInline { text: "using functionc all syntax" }
-
-            // Components can be generic too
-            // This component takes i32 type to give you typed input
-            TypedInput::<i32> {}
-
-            // Type inference can be used too
-            TypedInput { initial: 10.0 }
-
-            // geneircs with the `inline_props` macro
-            Label { text: "hello geneirc world!" }
-            Label { text: 99.9 }
-
-            // Lowercase components work too, as long as they are access using a path
-            baller::lowercase_component {}
-
-            // For in-scope lowercase components, use the `self` keyword
-            self::lowercase_helper {}
-
-            // helper functions
-            // Anything that implements IntoVnode can be dropped directly into Rsx
-            {helper(cx, "hello world!")}
-
-            // Strings can be supplied directly
-            {String::from("Hello world!")}
-
-            // So can format_args
-            {format_args!("Hello {}!", "world")}
-
-            // Or we can shell out to a helper function
-            {format_dollars(10, 50)}
-        }
-    })
-}
-
-fn format_dollars(dollars: u32, cents: u32) -> String {
-    format!("${dollars}.{cents:02}")
-}
-
-fn helper<'a>(cx: &'a ScopeState, text: &'a str) -> Element<'a> {
-    cx.render(rsx! {
-        p { "{text}" }
-    })
-}
-
-// no_case_check disables PascalCase checking if you *really* want a snake_case component.
-// This will likely be deprecated/removed in a future update that will introduce a more polished linting system,
-// something like Clippy.
-#[component(no_case_check)]
-fn lowercase_helper(cx: Scope) -> Element {
-    cx.render(rsx! {
-        "asd"
-    })
-}
-
-mod baller {
-    use super::*;
-    #[derive(Props, PartialEq, Eq)]
-    pub struct BallerProps {}
-
-    #[component]
-    /// This component totally balls
-    pub fn Baller(_cx: Scope<BallerProps>) -> Element {
-        todo!()
-    }
-
-    // no_case_check disables PascalCase checking if you *really* want a snake_case component.
-    // This will likely be deprecated/removed in a future update that will introduce a more polished linting system,
-    // something like Clippy.
-    #[component(no_case_check)]
-    pub fn lowercase_component(cx: Scope) -> Element {
-        cx.render(rsx! { "look ma, no uppercase" })
-    }
-}
-
-#[derive(Props)]
-pub struct TallerProps<'a> {
-    /// Fields are documented and accessible in rsx!
-    a: &'static str,
-    children: Element<'a>,
-}
-
-/// Documention for this component is visible within the rsx macro
-#[component]
-pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element {
-    cx.render(rsx! {
-        {&cx.props.children}
-    })
-}
-
-#[derive(Props, PartialEq, Eq)]
-pub struct TypedInputProps<T> {
-    #[props(optional, default)]
-    initial: Option<T>,
-}
-
-#[allow(non_snake_case)]
-pub fn TypedInput<T>(_: Scope<TypedInputProps<T>>) -> Element
-where
-    T: FromStr + fmt::Display,
-    <T as FromStr>::Err: std::fmt::Display,
-{
     todo!()
+    //launch_desktop(App);
 }
 
-#[component]
-fn WithInline<'a>(cx: Scope<'a>, text: &'a str) -> Element {
-    cx.render(rsx! {
-        p { "{text}" }
-    })
-}
-
-#[component]
-fn Label<T>(cx: Scope, text: T) -> Element
-where
-    T: Display,
-{
-    cx.render(rsx! {
-        p { "{text}" }
-    })
-}
+// use core::{fmt, str::FromStr};
+// use std::fmt::Display;
+
+// use baller::Baller;
+// use dioxus::prelude::*;
+
+// #[component]
+// fn App() -> Element {
+//     let formatting = "formatting!";
+//     let formatting_tuple = ("a", "b");
+//     let lazy_fmt = format_args!("lazily formatted text");
+//     let asd = 123;
+//     rsx! {
+//         div {
+//             // Elements
+//             div {}
+//             h1 {"Some text"}
+//             h1 {"Some text with {formatting}"}
+//             h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
+//             h1 {"Formatting without interpolation " {formatting_tuple.0} "and" {formatting_tuple.1} }
+//             h2 {
+//                 "Multiple"
+//                 "Text"
+//                 "Blocks"
+//                 "Use comments as separators in html"
+//             }
+//             div {
+//                 h1 {"multiple"}
+//                 h2 {"nested"}
+//                 h3 {"elements"}
+//             }
+//             div {
+//                 class: "my special div",
+//                 h1 {"Headers and attributes!"}
+//             }
+//             div {
+//                 // pass simple rust expressions in
+//                 class: lazy_fmt,
+//                 id: format_args!("attributes can be passed lazily with std::fmt::Arguments"),
+//                 class: "asd",
+//                 class: "{asd}",
+//                 // if statements can be used to conditionally render attributes
+//                 class: if formatting.contains("form") { "{asd}" },
+//                 div {
+//                     class: {
+//                         const WORD: &str = "expressions";
+//                         format_args!("Arguments can be passed in through curly braces for complex {WORD}")
+//                     }
+//                 }
+//             }
+
+//             // Expressions can be used in element position too:
+//             {rsx!(p { "More templating!" })},
+
+//             // Iterators
+//             {(0..10).map(|i| rsx!(li { "{i}" }))},
+
+//             // Iterators within expressions
+//             {
+//                 let data = std::collections::HashMap::<&'static str, &'static str>::new();
+//                 // Iterators *should* have keys when you can provide them.
+//                 // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
+//                 // Using an "ID" associated with your data is a good idea.
+//                 data.into_iter().map(|(k, v)| rsx!(li { key: "{k}", "{v}" }))
+//             }
+
+//             // Matching
+//             match true {
+//                 true => rsx!( h1 {"Top text"}),
+//                 false => rsx!( h1 {"Bottom text"})
+//             }
+
+//             // Conditional rendering
+//             // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
+//             // You can convert a bool condition to rsx! with .then and .or
+//             {true.then(|| rsx!(div {}))},
+
+//             // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element
+//             if false {
+//                 h1 {"Top text"}
+//             } else {
+//                 h1 {"Bottom text"}
+//             }
+
+//             // Using optionals for diverging branches
+//             // Note that since this is wrapped in curlies, it's interpreted as an expression
+//             {if true {
+//                 Some(rsx!(h1 {"Top text"}))
+//             } else {
+//                 None
+//             }}
+
+//             // returning "None" without a diverging branch is a bit noisy... but rare in practice
+//             {None as Option<()>},
+
+//             // can also just use empty fragments
+//             Fragment {}
+
+//             // Fragments let you insert groups of nodes without a parent.
+//             // This lets you make components that insert elements as siblings without a container.
+//             div {"A"}
+//             Fragment {
+//                 div {"B"}
+//                 div {"C"}
+//                 Fragment {
+//                     "D"
+//                     Fragment {
+//                         "E"
+//                         "F"
+//                     }
+//                 }
+//             }
+
+//             // Components
+//             // Can accept any paths
+//             // Notice how you still get syntax highlighting and IDE support :)
+//             Baller {}
+//             baller::Baller {}
+//             crate::baller::Baller {}
+
+//             // Can take properties
+//             Taller { a: "asd" }
+
+//             // Can take optional properties
+//             Taller { a: "asd" }
+
+//             // Can pass in props directly as an expression
+//             {
+//                 let props = TallerProps {a: "hello", children: None };
+//                 rsx!(Taller { ..props })
+//             }
+
+//             // Spreading can also be overridden manually
+//             Taller {
+//                 ..TallerProps { a: "ballin!", children: None },
+//                 a: "not ballin!"
+//             }
+
+//             // Can take children too!
+//             Taller { a: "asd", div {"hello world!"} }
+
+//             // This component's props are defined *inline* with the `inline_props` macro
+//             WithInline { text: "using functionc all syntax" }
+
+//             // Components can be generic too
+//             // This component takes i32 type to give you typed input
+//             TypedInput::<i32> {}
+
+//             // Type inference can be used too
+//             TypedInput { initial: 10.0 }
+
+//             // geneircs with the `inline_props` macro
+//             Label { text: "hello geneirc world!" }
+//             Label { text: 99.9 }
+
+//             // Lowercase components work too, as long as they are access using a path
+//             baller::lowercase_component {}
+
+//             // For in-scope lowercase components, use the `self` keyword
+//             self::lowercase_helper {}
+
+//             // helper functions
+//             // Anything that implements IntoVnode can be dropped directly into Rsx
+//             {helper("hello world!")}
+
+//             // Strings can be supplied directly
+//             {String::from("Hello world!")}
+
+//             // So can format_args
+//             {format_args!("Hello {}!", "world")}
+
+//             // Or we can shell out to a helper function
+//             {format_dollars(10, 50)}
+//         }
+//     }
+// }
+
+// fn format_dollars(dollars: u32, cents: u32) -> String {
+//     format!("${dollars}.{cents:02}")
+// }
+
+// fn helper<'a>(cx: &'a ScopeState, text: &'a str) -> Element {
+//     rsx! {
+//         p { "{text}" }
+//     }
+// }
+
+// // no_case_check disables PascalCase checking if you *really* want a snake_case component.
+// // This will likely be deprecated/removed in a future update that will introduce a more polished linting system,
+// // something like Clippy.
+// #[component(no_case_check)]
+// fn lowercase_helper() -> Element {
+//     rsx! {
+//         "asd"
+//     }
+// }
+
+// mod baller {
+//     use super::*;
+
+//     #[component]
+//     /// This component totally balls
+//     pub fn Baller() -> Element {
+//         todo!()
+//     }
+
+//     // no_case_check disables PascalCase checking if you *really* want a snake_case component.
+//     // This will likely be deprecated/removed in a future update that will introduce a more polished linting system,
+//     // something like Clippy.
+//     #[component(no_case_check)]
+//     pub fn lowercase_component() -> Element {
+//         rsx! { "look ma, no uppercase" }
+//     }
+// }
+
+// /// Documention for this component is visible within the rsx macro
+// #[component]
+// pub fn Taller(
+//     /// Fields are documented and accessible in rsx!
+//     a: &'static str,
+//     children: Element,
+// ) -> Element {
+//     rsx! { {&children} }
+// }
+
+// #[derive(Props, PartialEq, Eq)]
+// pub struct TypedInputProps<T> {
+//     #[props(optional, default)]
+//     initial: Option<T>,
+// }
+
+// #[allow(non_snake_case)]
+// pub fn TypedInput<T>(_: Scope<TypedInputProps<T>>) -> Element
+// where
+//     T: FromStr + fmt::Display,
+//     <T as FromStr>::Err: std::fmt::Display,
+// {
+//     todo!()
+// }
+
+// #[component]
+// fn WithInline(cx: Scope<'a>, text: &'a str) -> Element {
+//     rsx! {
+//         p { "{text}" }
+//     }
+// }
+
+// #[component]
+// fn Label<T: Clone + PartialEq>(text: T) -> Element
+// where
+//     T: Display,
+// {
+//     rsx! {
+//         p { "{text}" }
+//     }
+// }

+ 9 - 13
examples/scroll_to_top.rs

@@ -1,18 +1,16 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let header_element = use_ref(cx, || None);
+fn app() -> Element {
+    let mut header_element = use_signal(|| None);
 
-    cx.render(rsx!(
+    rsx! {
         div {
             h1 {
-                onmounted: move |cx| {
-                    header_element.set(Some(cx.inner().clone()));
-                },
+                onmounted: move |cx| header_element.set(Some(cx.data())),
                 "Scroll to top example"
             }
 
@@ -21,15 +19,13 @@ fn app(cx: Scope) -> Element {
             }
 
             button {
-                onclick: move |_| {
-                    if let Some(header) = header_element.read().as_ref().cloned() {
-                        cx.spawn(async move {
-                            let _ = header.scroll_to(ScrollBehavior::Smooth).await;
-                        });
+                onclick: move |_| async move {
+                    if let Some(header) = header_element.cloned() {
+                        let _ = header.scroll_to(ScrollBehavior::Smooth).await;
                     }
                 },
                 "Scroll to top"
             }
         }
-    ))
+    }
 }

+ 0 - 73
examples/shared_state.rs

@@ -1,73 +0,0 @@
-use std::collections::HashMap;
-
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus_desktop::launch(App);
-}
-
-#[derive(Default)]
-struct CoolData {
-    data: HashMap<usize, String>,
-}
-
-impl CoolData {
-    pub fn new(data: HashMap<usize, String>) -> Self {
-        Self { data }
-    }
-
-    pub fn view(&self, id: &usize) -> Option<&String> {
-        self.data.get(id)
-    }
-
-    pub fn set(&mut self, id: usize, data: String) {
-        self.data.insert(id, data);
-    }
-}
-
-#[component]
-#[rustfmt::skip]
-pub fn App(cx: Scope) -> Element {
-    use_shared_state_provider(cx, || CoolData::new(HashMap::from([
-        (0, "Hello, World!".to_string()),
-        (1, "Dioxus is amazing!".to_string())
-    ])));
-
-    render!(
-        DataEditor {
-            id: 0
-        }
-        DataEditor {
-            id: 1
-        }
-        DataView {
-            id: 0
-        }
-        DataView {
-            id: 1
-        }
-    )
-}
-
-#[component]
-fn DataEditor(cx: Scope, id: usize) -> Element {
-    let data = use_shared_state::<CoolData>(cx)?;
-
-    render! {
-        p {
-            {data.read().view(id)?}
-        }
-    }
-}
-
-#[component]
-fn DataView(cx: Scope, id: usize) -> Element {
-    let data = use_shared_state::<CoolData>(cx)?;
-
-    render! {
-        input {
-            oninput: move |e: FormEvent| data.write().set(*id, e.value()),
-            value: data.read().view(id)?
-        }
-    }
-}

+ 6 - 9
examples/shortcut.rs

@@ -1,17 +1,14 @@
+use dioxus::desktop::use_global_shortcut;
 use dioxus::prelude::*;
-use dioxus_desktop::use_global_shortcut;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let toggled = use_state(cx, || false);
+fn app() -> Element {
+    let mut toggled = use_signal(|| false);
 
-    use_global_shortcut(cx, "ctrl+s", {
-        to_owned![toggled];
-        move || toggled.modify(|t| !*t)
-    });
+    _ = use_global_shortcut("ctrl+s", move || toggled.toggle());
 
-    cx.render(rsx!("toggle: {toggled.get()}"))
+    rsx!("toggle: {toggled}")
 }

+ 7 - 16
examples/shorthand.rs

@@ -1,10 +1,10 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
+fn app() -> Element {
     let a = 123;
     let b = 456;
     let c = 789;
@@ -14,10 +14,10 @@ fn app(cx: Scope) -> Element {
     // todo: i'd like it for children on elements to be inferred as the children of the element
     // also should shorthands understand references/dereferences?
     // ie **a, *a, &a, &mut a, etc
-    let children = render! { "Child" };
+    let children = rsx! { "Child" };
     let onclick = move |_| println!("Clicked!");
 
-    render! {
+    rsx! {
         div { class, id, {&children} }
         Component { a, b, c, children, onclick }
         Component { a, ..ComponentProps { a: 1, b: 2, c: 3, children: None, onclick: Default::default() } }
@@ -25,21 +25,12 @@ fn app(cx: Scope) -> Element {
 }
 
 #[component]
-fn Component<'a>(
-    cx: Scope<'a>,
-    a: i32,
-    b: i32,
-    c: i32,
-    children: Element<'a>,
-    onclick: EventHandler<'a, ()>,
-) -> Element {
-    render! {
+fn Component(a: i32, b: i32, c: i32, children: Element, onclick: EventHandler) -> Element {
+    rsx! {
         div { "{a}" }
         div { "{b}" }
         div { "{c}" }
         div { {children} }
-        div {
-            onclick: move |_| onclick.call(()),
-        }
+        div { onclick: move |_| onclick.call(()) }
     }
 }

+ 48 - 15
examples/signals.rs

@@ -2,40 +2,60 @@ use dioxus::prelude::*;
 use std::time::Duration;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let running = dioxus_signals::use_signal(cx, || true);
-    let mut count = dioxus_signals::use_signal(cx, || 0);
-    let saved_values = dioxus_signals::use_signal(cx, || vec![0.to_string()]);
+fn app() -> Element {
+    let mut running = use_signal(|| true);
+    let mut count = use_signal(|| 0);
+    let mut saved_values = use_signal(|| vec![0.to_string()]);
 
-    // Signals can be used in async functions without an explicit clone since they're 'static and Copy
-    // Signals are backed by a runtime that is designed to deeply integrate with Dioxus apps
-    use_future!(cx, || async move {
+    // use_memo will recompute the value of the signal whenever the captured signals change
+    let doubled_count = use_memo(move || count() * 2);
+
+    // use_effect will subscribe to any changes in the signal values it captures
+    // effects will always run after first mount and then whenever the signal values change
+    use_effect(move || println!("Count changed to {count}"));
+
+    // We can do early returns and conditional rendering which will pause all futures that haven't been polled
+    if count() > 30 {
+        return rsx! {
+            h1 { "Count is too high!" }
+            button { onclick: move |_| count.set(0), "Press to reset" }
+        };
+    }
+
+    // use_future will spawn an infinitely running future that can be started and stopped
+    use_future(move || async move {
         loop {
-            if running.value() {
+            if running() {
                 count += 1;
             }
             tokio::time::sleep(Duration::from_millis(400)).await;
         }
     });
 
-    cx.render(rsx! {
+    // use_resource will spawn a future that resolves to a value
+    let _slow_count = use_resource(move || async move {
+        tokio::time::sleep(Duration::from_millis(200)).await;
+        count() * 2
+    });
+
+    rsx! {
         h1 { "High-Five counter: {count}" }
         button { onclick: move |_| count += 1, "Up high!" }
         button { onclick: move |_| count -= 1, "Down low!" }
         button { onclick: move |_| running.toggle(), "Toggle counter" }
-        button { onclick: move |_| saved_values.push(count.value().to_string()), "Save this value" }
-        button { onclick: move |_| saved_values.write().clear(), "Clear saved values" }
+        button { onclick: move |_| saved_values.push(count.to_string()), "Save this value" }
+        button { onclick: move |_| saved_values.clear(), "Clear saved values" }
 
         // We can do boolean operations on the current signal value
-        if count.value() > 5 {
+        if count() > 5 {
             h2 { "High five!" }
         }
 
         // We can cleanly map signals with iterators
-        for value in saved_values.read().iter() {
+        for value in saved_values.iter() {
             h3 { "Saved value: {value}" }
         }
 
@@ -45,5 +65,18 @@ fn app(cx: Scope) -> Element {
         } else {
             "No saved values"
         }
-    })
+
+        // You can pass a value directly to any prop that accepts a signal
+        Child { count: doubled_count() }
+        Child { count: doubled_count }
+    }
+}
+
+#[component]
+fn Child(mut count: ReadOnlySignal<i32>) -> Element {
+    println!("rendering child with count {count}");
+
+    rsx! {
+        h1 { "{count}" }
+    }
 }

+ 0 - 82
examples/simple_desktop.rs

@@ -1,82 +0,0 @@
-#![allow(non_snake_case)]
-
-use dioxus::prelude::*;
-use dioxus_router::prelude::*;
-
-fn main() {
-    simple_logger::SimpleLogger::new()
-        .with_level(log::LevelFilter::Debug)
-        .with_module_level("dioxus", log::LevelFilter::Trace)
-        .init()
-        .unwrap();
-    dioxus_desktop::launch(App);
-}
-
-#[component]
-fn App(cx: Scope) -> Element {
-    render! {
-        Router::<Route> {}
-    }
-}
-
-#[derive(Routable, Clone)]
-#[rustfmt::skip]
-enum Route {
-    #[layout(NavBar)]
-        #[route("/")]
-        Home {},
-        #[nest("/new")]
-            #[route("/")]
-            BlogList {},
-            #[route("/:post")]
-            BlogPost {
-                post: String,
-            },
-        #[end_nest]
-        #[route("/oranges")]
-        Oranges {},
-}
-
-#[component]
-fn NavBar(cx: Scope) -> Element {
-    render! {
-        h1 { "Your app here" }
-        ul {
-            li { Link { to: Route::Home {}, "home" } }
-            li { Link { to: Route::BlogList {}, "blog" } }
-            li { Link { to: Route::BlogPost { post: "tim".into() }, "tims' blog" } }
-            li { Link { to: Route::BlogPost { post: "bill".into() }, "bills' blog" } }
-            li { Link { to: Route::BlogPost { post: "james".into() }, "james amazing' blog" } }
-        }
-        Outlet::<Route> {}
-    }
-}
-
-#[component]
-fn Home(cx: Scope) -> Element {
-    log::debug!("rendering home {:?}", cx.scope_id());
-    render! { h1 { "Home" } }
-}
-
-#[component]
-fn BlogList(cx: Scope) -> Element {
-    log::debug!("rendering blog list {:?}", cx.scope_id());
-    render! { div { "Blog List" } }
-}
-
-#[component]
-fn BlogPost(cx: Scope, post: String) -> Element {
-    log::debug!("rendering blog post {}", post);
-
-    render! {
-        div {
-            h3 { "blog post: {post}"  }
-            Link { to: Route::BlogList {}, "back to blog list" }
-        }
-    }
-}
-
-#[component]
-fn Oranges(cx: Scope) -> Element {
-    render!("Oranges are not apples!")
-}

+ 5 - 5
examples/simple_list.rs

@@ -1,11 +1,11 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx!(
+fn app() -> Element {
+    rsx!(
         div {
             // Use Map directly to lazily pull elements
             {(0..10).map(|f| rsx! { "{f}" })},
@@ -22,7 +22,7 @@ fn app(cx: Scope) -> Element {
 
             // use a for loop where the body itself is RSX
             for name in 0..10 {
-                div {"{name}"}
+                div { "{name}" }
             }
 
             // Or even use an unterminated conditional
@@ -30,5 +30,5 @@ fn app(cx: Scope) -> Element {
                 "hello world!"
             }
         }
-    ))
+    )
 }

+ 27 - 11
examples/simple_router.rs

@@ -1,6 +1,6 @@
 #![allow(non_snake_case)]
+
 use dioxus::prelude::*;
-use dioxus_router::prelude::*;
 
 #[derive(Routable, Clone, PartialEq)]
 enum Route {
@@ -13,30 +13,46 @@ enum Route {
 }
 
 #[component]
-fn Homepage(cx: Scope) -> Element {
-    render! { h1 { "Welcome home" } }
+fn Homepage() -> Element {
+    rsx! { h1 { "Welcome home" } }
 }
 
 #[component]
-fn Blog(cx: Scope, id: String) -> Element {
-    render! {
+fn Blog(id: String) -> Element {
+    rsx! {
         h1 { "How to make: " }
         p { "{id}" }
     }
 }
 
 #[component]
-fn Nav(cx: Scope) -> Element {
-    render! {
+fn Nav() -> Element {
+    rsx! {
         nav {
-            li { Link { to: Route::Homepage { }, "Go home" } }
-            li { Link { to: Route::Blog { id: "Brownies".to_string() }, "Learn Brownies" } }
-            li { Link { to: Route::Blog { id: "Cookies".to_string() }, "Learn Cookies"  } }
+            li {
+                Link { to: Route::Homepage {}, "Go home" }
+            }
+            li {
+                Link {
+                    to: Route::Blog {
+                        id: "Brownies".to_string(),
+                    },
+                    "Learn Brownies"
+                }
+            }
+            li {
+                Link {
+                    to: Route::Blog {
+                        id: "Cookies".to_string(),
+                    },
+                    "Learn Cookies"
+                }
+            }
         }
         div { Outlet::<Route> {} }
     }
 }
 
 fn main() {
-    dioxus_desktop::launch(|cx| render!(Router::<Route> {}));
+    launch_desktop(|| rsx! { Router::<Route> {} });
 }

+ 16 - 16
examples/spread.rs

@@ -1,16 +1,15 @@
 use dioxus::prelude::*;
 
 fn main() {
-    let mut dom = VirtualDom::new(app);
-    let _ = dom.rebuild();
+    let dom = VirtualDom::prebuilt(app);
     let html = dioxus_ssr::render(&dom);
 
     println!("{}", html);
 }
 
-fn app(cx: Scope) -> Element {
-    render! {
-        Component {
+fn app() -> Element {
+    rsx! {
+        spreadable_component {
             width: "10px",
             extra_data: "hello{1}",
             extra_data2: "hello{2}",
@@ -20,17 +19,18 @@ fn app(cx: Scope) -> Element {
     }
 }
 
-#[component]
-fn Component<'a>(cx: Scope<'a, Props<'a>>) -> Element<'a> {
-    render! {
-        audio { ..cx.props.attributes, "1: {cx.props.extra_data}\n2: {cx.props.extra_data2}" }
-    }
+#[derive(Props, PartialEq, Clone)]
+struct Props {
+    #[props(extends = GlobalAttributes)]
+    attributes: Vec<Attribute>,
+
+    extra_data: String,
+
+    extra_data2: String,
 }
 
-#[derive(Props)]
-struct Props<'a> {
-    #[props(extends = GlobalAttributes)]
-    attributes: Vec<Attribute<'a>>,
-    extra_data: &'a str,
-    extra_data2: &'a str,
+fn spreadable_component(props: Props) -> Element {
+    rsx! {
+        audio { ..props.attributes, "1: {props.extra_data}\n2: {props.extra_data2}" }
+    }
 }

+ 5 - 6
examples/ssr.rs

@@ -6,14 +6,13 @@ use dioxus::prelude::*;
 
 fn main() {
     // We can render VirtualDoms
-    let mut vdom = VirtualDom::new(app);
-    let _ = vdom.rebuild();
+    let vdom = VirtualDom::prebuilt(app);
     println!("{}", dioxus_ssr::render(&vdom));
 
     // Or we can render rsx! calls themselves
     println!(
         "{}",
-        dioxus_ssr::render_lazy(rsx! {
+        dioxus_ssr::render_element(rsx! {
             div {
                 h1 { "Hello, world!" }
             }
@@ -30,11 +29,11 @@ fn main() {
     println!("{file}");
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx!(
+fn app() -> Element {
+    rsx!(
         div {
             h1 { "Title" }
             p { "Body" }
         }
-    ))
+    )
 }

+ 36 - 0
examples/stale_memo.rs

@@ -0,0 +1,36 @@
+use dioxus::prelude::*;
+
+fn main() {
+    launch_desktop(app);
+}
+
+fn app() -> Element {
+    let mut state = use_signal(|| 0);
+    let mut depth = use_signal(|| 1_usize);
+
+    if depth() == 5 {
+        return rsx! {
+            div { "Max depth reached" }
+            button { onclick: move |_| depth -= 1, "Remove depth" }
+        };
+    }
+
+    let items = use_memo(move || (0..depth()).map(|f| f as _).collect::<Vec<isize>>());
+
+    rsx! {
+        button { onclick: move |_| state += 1, "Increment" }
+        button { onclick: move |_| depth += 1, "Add depth" }
+        button {
+            onclick: move |_| async move {
+                depth += 1;
+                tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+                dbg!(items.read());
+                // if depth() is 5, this will be the old since the memo hasn't been re-computed
+                // use_memos are only re-computed when the signals they capture change
+                // *and* they are used in the current render
+                // If the use_memo isn't used, it can't be re-computed!
+            },
+            "Add depth with sleep"
+        }
+    }
+}

+ 6 - 7
examples/streams.rs

@@ -1,16 +1,15 @@
 use dioxus::prelude::*;
-use dioxus_signals::use_signal;
 use futures_util::{future, stream, Stream, StreamExt};
 use std::time::Duration;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let count = use_signal(cx, || 10);
+fn app() -> Element {
+    let mut count = use_signal(|| 10);
 
-    use_future(cx, (), |_| async move {
+    use_future(move || async move {
         let mut stream = some_stream();
 
         while let Some(second) = stream.next().await {
@@ -18,9 +17,9 @@ fn app(cx: Scope) -> Element {
         }
     });
 
-    cx.render(rsx! {
+    rsx! {
         h1 { "High-Five counter: {count}" }
-    })
+    }
 }
 
 fn some_stream() -> std::pin::Pin<Box<dyn Stream<Item = i32>>> {

+ 26 - 33
examples/suspense.rs

@@ -13,28 +13,25 @@
 //! We can achieve the majority of suspense functionality by composing "suspenseful"
 //! primitives in our own custom components.
 
+use dioxus::desktop::{Config, LogicalSize, WindowBuilder};
 use dioxus::prelude::*;
-use dioxus_desktop::{Config, LogicalSize, WindowBuilder};
 
 fn main() {
-    let cfg = Config::new().with_window(
-        WindowBuilder::new()
-            .with_title("Doggo Fetcher")
-            .with_inner_size(LogicalSize::new(600.0, 800.0)),
-    );
-
-    dioxus_desktop::launch_cfg(app, cfg);
-}
-
-#[derive(serde::Deserialize)]
-struct DogApi {
-    message: String,
+    LaunchBuilder::desktop()
+        .with_cfg(
+            Config::new().with_window(
+                WindowBuilder::new()
+                    .with_title("Doggo Fetcher")
+                    .with_inner_size(LogicalSize::new(600.0, 800.0)),
+            ),
+        )
+        .launch(app)
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         div {
-            h1 {"Dogs are very important"}
+            h1 { "Dogs are very important" }
             p {
                 "The dog or domestic dog (Canis familiaris[4][5] or Canis lupus familiaris[5])"
                 "is a domesticated descendant of the wolf which is characterized by an upturning tail."
@@ -44,16 +41,21 @@ fn app(cx: Scope) -> Element {
             }
 
             h3 { "Illustrious Dog Photo" }
-            Doggo { }
+            Doggo {}
         }
-    })
+    }
 }
 
 /// This component will re-render when the future has finished
 /// Suspense is achieved my moving the future into only the component that
 /// actually renders the data.
-fn Doggo(cx: Scope) -> Element {
-    let fut = use_future(cx, (), |_| async move {
+fn Doggo() -> Element {
+    let mut fut = use_resource(move || async move {
+        #[derive(serde::Deserialize)]
+        struct DogApi {
+            message: String,
+        }
+
         reqwest::get("https://dog.ceo/api/breeds/image/random/")
             .await
             .unwrap()
@@ -61,21 +63,12 @@ fn Doggo(cx: Scope) -> Element {
             .await
     });
 
-    cx.render(match fut.value() {
+    match fut.read().as_ref() {
         Some(Ok(resp)) => rsx! {
-            button {
-                onclick: move |_| fut.restart(),
-                "Click to fetch another doggo"
-            }
-            div {
-                img {
-                    max_width: "500px",
-                    max_height: "500px",
-                    src: "{resp.message}",
-                }
-            }
+            button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
+            div { img { max_width: "500px", max_height: "500px", src: "{resp.message}" } }
         },
         Some(Err(_)) => rsx! { div { "loading dogs failed" } },
         None => rsx! { div { "loading dogs..." } },
-    })
+    }
 }

+ 35 - 88
examples/svg.rs

@@ -1,103 +1,50 @@
 // Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
 
 use dioxus::prelude::*;
+use rand::{thread_rng, Rng};
 
 fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let val = use_state(cx, || 5);
-
-    cx.render(rsx! {
-        div {
-            user_select: "none",
-            webkit_user_select: "none",
-            margin_left: "10%",
-            margin_right: "10%",
-            h1 { "Click die to generate a new value" }
-            div {
-                cursor: "pointer",
-                height: "80%",
-                width: "80%",
-                Die {
-                    value: **val,
-                    keep: true,
-                    onclick: move |_| {
-                        use rand::Rng;
-                        let mut rng = rand::thread_rng();
-                        val.set(rng.gen_range(1..=6));
-                    }
-                }
+    launch(|| {
+        rsx! {
+            div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%",
+                h1 { "Click die to generate a new value" }
+                div { cursor: "pointer", height: "100%", width: "100%", Dice {} }
             }
         }
-    })
+    });
 }
 
-#[derive(Props)]
-pub struct DieProps<'a> {
-    pub value: u64,
-    pub keep: bool,
-    pub onclick: EventHandler<'a, MouseEvent>,
-}
-
-const DOTS: [(i64, i64); 7] = [(-1, -1), (-1, -0), (-1, 1), (1, -1), (1, 0), (1, 1), (0, 0)];
-const DOTS_FOR_VALUE: [[bool; 7]; 6] = [
-    [false, false, false, false, false, false, true],
-    [false, false, true, true, false, false, false],
-    [false, false, true, true, false, false, true],
-    [true, false, true, true, false, true, false],
-    [true, false, true, true, false, true, true],
-    [true, true, true, true, true, true, false],
-];
-
-const OFFSET: i64 = 600;
-const DOT_RADIUS: &str = "200";
-const HELD_COLOR: &str = "#aaa";
-const UNHELD_COLOR: &str = "#ddd";
-
-// A six-sided die (D6) with dots.
-#[allow(non_snake_case)]
-pub fn Die<'a>(cx: Scope<'a, DieProps<'a>>) -> Element {
-    let &DieProps { value, keep, .. } = cx.props;
-
-    let active_dots = &DOTS_FOR_VALUE[(value - 1) as usize];
-    let fill = if keep { HELD_COLOR } else { UNHELD_COLOR };
-    let dots = DOTS
-        .iter()
-        .zip(active_dots.iter())
-        .filter(|(_, &active)| active)
-        .map(|((x, y), _)| {
-            let dcx = x * OFFSET;
-            let dcy = y * OFFSET;
-
-            rsx! {
+#[component]
+fn Dice() -> Element {
+    const Y: bool = true;
+    const N: bool = false;
+    const DOTS: [(i64, i64); 7] = [(-1, -1), (-1, -0), (-1, 1), (1, -1), (1, 0), (1, 1), (0, 0)];
+    const DOTS_FOR_VALUE: [[bool; 7]; 6] = [
+        [N, N, N, N, N, N, Y],
+        [N, N, Y, Y, N, N, N],
+        [N, N, Y, Y, N, N, Y],
+        [Y, N, Y, Y, N, Y, N],
+        [Y, N, Y, Y, N, Y, Y],
+        [Y, Y, Y, Y, Y, Y, N],
+    ];
+
+    let mut value = use_signal(|| 5);
+    let active_dots = use_memo(move || &DOTS_FOR_VALUE[(value() - 1) as usize]);
+
+    rsx! {
+        svg {
+            view_box: "-1000 -1000 2000 2000",
+            prevent_default: "onclick",
+            onclick: move |_| value.set(thread_rng().gen_range(1..=6)),
+            rect { x: -1000, y: -1000, width: 2000, height: 2000, rx: 200, fill: "#aaa" }
+            for ((x, y), _) in DOTS.iter().zip(active_dots.read().iter()).filter(|(_, &active)| active) {
                 circle {
-                    cx: "{dcx}",
-                    cy: "{dcy}",
-                    r: "{DOT_RADIUS}",
+                    cx: *x * 600,
+                    cy: *y * 600,
+                    r: 200,
                     fill: "#333"
                 }
             }
-        });
-
-    cx.render(rsx! {
-      svg {
-        onclick: move |e| cx.props.onclick.call(e),
-        prevent_default: "onclick",
-        class: "die",
-        view_box: "-1000 -1000 2000 2000",
-
-        rect {
-          x: "-1000",
-          y: "-1000",
-          width: "2000",
-          height: "2000",
-          rx: "{DOT_RADIUS}",
-          fill: "{fill}",
         }
-
-        {dots}
-      }
-    })
+    }
 }

+ 0 - 81
examples/svg_basic.rs

@@ -1,81 +0,0 @@
-use dioxus::prelude::*;
-
-fn app(cx: Scope) -> Element {
-    cx.render(rsx!( svg {
-        width: "200",
-        height: "250",
-        xmlns: "http://www.w3.org/2000/svg",
-        version: "1.1",
-        rect {
-            x: "10",
-            y: "10",
-            width: "30",
-            height: "30",
-            stroke: "black",
-            fill: "transparent",
-            stroke_width: "5",
-        }
-        rect {
-            x: "60",
-            y: "10",
-            width: "30",
-            height: "30",
-            stroke: "black",
-            fill: "transparent",
-            stroke_width: "5",
-        }
-        circle {
-            cx: "25",
-            cy: "75",
-            r: "20",
-            stroke: "red",
-            fill: "transparent",
-            stroke_width: "5",
-        }
-        ellipse {
-            cx: "75",
-            cy: "75",
-            rx: "20",
-            ry: "5",
-            stroke: "red",
-            fill: "transparent",
-            stroke_width: "5",
-        }
-        line {
-            x1: "10",
-            x2: "50",
-            y1: "110",
-            y2: "150",
-            stroke: "orange",
-            stroke_width: "5",
-        }
-        polyline {
-            points: "60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145",
-            stroke: "orange",
-            fill: "transparent",
-            stroke_width: "5",
-        }
-        polygon {
-            points: "50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180",
-            stroke: "green",
-            fill: "transparent",
-            stroke_width: "5",
-        }
-        path {
-            d: "M20,230 Q40,205 50,230 T90,230",
-            fill: "none",
-            stroke: "blue",
-            stroke_width: "5",
-        }
-        path {
-            d: "M9.00001 9C9 62 103.5 124 103.5 178",
-            stroke: "#3CC4DC",
-            "stroke-linecap": "square",
-            "stroke-width": "square",
-        }
-    }))
-}
-
-fn main() {
-    dioxus_desktop::launch(app);
-}

+ 2 - 3
examples/tailwind/Cargo.toml

@@ -12,11 +12,10 @@ rust-version = "1.60.0"
 publish = false
 
 [dependencies]
-dioxus = { path = "../../packages/dioxus" }
 manganis = { workspace = true }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-dioxus-desktop = { path = "../../packages/desktop" }
+dioxus = { path = "../../packages/dioxus", features = ["desktop"] }
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
-dioxus-web = { path = "../../packages/web" }
+dioxus = { path = "../../packages/dioxus", features = ["web"] }

+ 0 - 1
examples/tailwind/dist/tailwind3531548035813279582.css

@@ -1 +0,0 @@
-*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html{-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{color:inherit;border-top-width:1px;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (width>=640px){.container{max-width:640px}}@media (width>=768px){.container{max-width:768px}}@media (width>=1024px){.container{max-width:1024px}}@media (width>=1280px){.container{max-width:1280px}}@media (width>=1536px){.container{max-width:1536px}}.mx-auto{margin-left:auto;margin-right:auto}.mb-16{margin-bottom:4rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-5{margin-right:1.25rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-10{height:2.5rem}.h-4{height:1rem}.w-10{width:2.5rem}.w-4{width:1rem}.w-5\/6{width:83.3333%}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border-0{border-width:0}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.object-cover{-o-object-fit:cover;object-fit:cover}.object-center{-o-object-position:center;object-position:center}.p-2{padding:.5rem}.p-5{padding:1.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-24{padding-top:6rem;padding-bottom:6rem}.text-center{text-align:center}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-medium{font-weight:500}.leading-relaxed{line-height:1.625}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:outline-none:focus{outline-offset:2px;outline:2px solid #0000}@media (width>=640px){.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}}@media (width>=768px){.md\:mb-0{margin-bottom:0}.md\:ml-auto{margin-left:auto}.md\:mt-0{margin-top:0}.md\:w-1\/2{width:50%}.md\:flex-row{flex-direction:row}.md\:items-start{align-items:flex-start}.md\:pr-16{padding-right:4rem}.md\:text-left{text-align:left}}@media (width>=1024px){.lg\:inline-block{display:inline-block}.lg\:w-full{width:100%}.lg\:max-w-lg{max-width:32rem}.lg\:flex-grow{flex-grow:1}.lg\:pr-24{padding-right:6rem}}

+ 27 - 33
examples/tailwind/src/main.rs

@@ -2,36 +2,34 @@
 
 use dioxus::prelude::*;
 
-const _STYLE: &str = manganis::mg!(file("./public/tailwind.css"));
+const _STYLE: &str = manganis::mg!(file("public/tailwind.css"));
 
 fn main() {
-    #[cfg(not(target_arch = "wasm32"))]
-    dioxus_desktop::launch(app);
-    #[cfg(target_arch = "wasm32")]
-    dioxus_web::launch(app);
+    launch(app);
 }
 
-pub fn app(cx: Scope) -> Element {
+pub fn app() -> Element {
     let grey_background = true;
-    cx.render(rsx!(
+    rsx!(
         div {
             header {
                 class: "text-gray-400 body-font",
                 // you can use optional attributes to optionally apply a tailwind class
-                class: if grey_background { "bg-gray-900" },
+                class: if grey_background {
+                    "bg-gray-900"
+                },
                 div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center",
                     a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0",
                         StacksIcon {}
-                        span { class: "ml-3 text-xl", "Hello Dioxus!"}
+                        span { class: "ml-3 text-xl", "Hello Dioxus!" }
                     }
                     nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center",
-                        a { class: "mr-5 hover:text-white", "First Link"}
-                        a { class: "mr-5 hover:text-white", "Second Link"}
-                        a { class: "mr-5 hover:text-white", "Third Link"}
-                        a { class: "mr-5 hover:text-white", "Fourth Link"}
+                        a { class: "mr-5 hover:text-white", "First Link" }
+                        a { class: "mr-5 hover:text-white", "Second Link" }
+                        a { class: "mr-5 hover:text-white", "Third Link" }
+                        a { class: "mr-5 hover:text-white", "Fourth Link" }
                     }
-                    button {
-                        class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
+                    button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
                         "Button"
                         RightArrowIcon {}
                     }
@@ -45,21 +43,17 @@ pub fn app(cx: Scope) -> Element {
                             br { class: "hidden lg:inline-block" }
                             "Dioxus Sneak Peek"
                         }
-                        p {
-                            class: "mb-8 leading-relaxed",
+                        p { class: "mb-8 leading-relaxed",
 
                             "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web
                             technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
                             on mobile and embedded platforms."
-
                         }
                         div { class: "flex justify-center",
-                            button {
-                                class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg",
+                            button { class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg",
                                 "Learn more"
                             }
-                            button {
-                                class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg",
+                            button { class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg",
                                 "Build an app"
                             }
                         }
@@ -68,18 +62,18 @@ pub fn app(cx: Scope) -> Element {
                         img {
                             class: "object-cover object-center rounded",
                             src: "https://i.imgur.com/oK6BLtw.png",
-                            referrerpolicy:"no-referrer",
-                            alt: "hero",
+                            referrerpolicy: "no-referrer",
+                            alt: "hero"
                         }
                     }
                 }
             }
         }
-    ))
+    )
 }
 
-pub fn StacksIcon(cx: Scope) -> Element {
-    cx.render(rsx!(
+pub fn StacksIcon() -> Element {
+    rsx!(
         svg {
             fill: "none",
             stroke: "currentColor",
@@ -88,13 +82,13 @@ pub fn StacksIcon(cx: Scope) -> Element {
             stroke_width: "2",
             class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full",
             view_box: "0 0 24 24",
-            path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
+            path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" }
         }
-    ))
+    )
 }
 
-pub fn RightArrowIcon(cx: Scope) -> Element {
-    cx.render(rsx!(
+pub fn RightArrowIcon() -> Element {
+    rsx!(
         svg {
             fill: "none",
             stroke: "currentColor",
@@ -103,7 +97,7 @@ pub fn RightArrowIcon(cx: Scope) -> Element {
             stroke_width: "2",
             class: "w-4 h-4 ml-1",
             view_box: "0 0 24 24",
-            path { d: "M5 12h14M12 5l7 7-7 7"}
+            path { d: "M5 12h14M12 5l7 7-7 7" }
         }
-    ))
+    )
 }

+ 10 - 16
examples/tasks.rs

@@ -6,29 +6,23 @@ use dioxus::prelude::*;
 use std::time::Duration;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let count = use_state(cx, || 0);
+fn app() -> Element {
+    let mut count = use_signal(|| 0);
 
-    use_future(cx, (), move |_| {
-        let mut count = count.clone();
-        async move {
-            loop {
-                tokio::time::sleep(Duration::from_millis(1000)).await;
-                count += 1;
-            }
+    use_future(move || async move {
+        loop {
+            tokio::time::sleep(Duration::from_millis(1000)).await;
+            count += 1;
         }
     });
 
-    cx.render(rsx! {
+    rsx! {
         div {
             h1 { "Current count: {count}" }
-            button {
-                onclick: move |_| count.set(0),
-                "Reset the count"
-            }
+            button { onclick: move |_| count.set(0), "Reset the count" }
         }
-    })
+    }
 }

+ 5 - 7
examples/textarea.rs

@@ -3,15 +3,13 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let model = use_state(cx, || String::from("asd"));
+fn app() -> Element {
+    let mut model = use_signal(|| String::from("asd"));
 
-    println!("{model}");
-
-    cx.render(rsx! {
+    rsx! {
         textarea {
             class: "border",
             rows: "10",
@@ -19,5 +17,5 @@ fn app(cx: Scope) -> Element {
             value: "{model}",
             oninput: move |e| model.set(e.value().clone()),
         }
-    })
+    }
 }

+ 111 - 144
examples/todomvc.rs

@@ -1,104 +1,108 @@
 #![allow(non_snake_case)]
-
 use dioxus::prelude::*;
 use dioxus_elements::input_data::keyboard_types::Key;
+use std::collections::HashMap;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 #[derive(PartialEq, Eq, Clone, Copy)]
-pub enum FilterState {
+enum FilterState {
     All,
     Active,
     Completed,
 }
 
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct TodoItem {
-    pub id: u32,
-    pub checked: bool,
-    pub contents: String,
+#[derive(Debug, PartialEq, Eq)]
+struct TodoItem {
+    id: u32,
+    checked: bool,
+    contents: String,
 }
 
-pub fn app(cx: Scope<()>) -> Element {
-    let todos = use_state(cx, im_rc::HashMap::<u32, TodoItem>::default);
-    let filter = use_state(cx, || FilterState::All);
+const STYLE: &str = include_str!("./assets/todomvc.css");
 
-    // Filter the todos based on the filter state
-    let mut filtered_todos = todos
-        .iter()
-        .filter(|(_, item)| match **filter {
-            FilterState::All => true,
-            FilterState::Active => !item.checked,
-            FilterState::Completed => item.checked,
-        })
-        .map(|f| *f.0)
-        .collect::<Vec<_>>();
-    filtered_todos.sort_unstable();
+fn app() -> Element {
+    let mut todos = use_signal(HashMap::<u32, TodoItem>::new);
+    let filter = use_signal(|| FilterState::All);
 
-    let active_todo_count = todos.values().filter(|item| !item.checked).count();
-    let active_todo_text = match active_todo_count {
-        1 => "item",
-        _ => "items",
-    };
+    let active_todo_count =
+        use_memo(move || todos.read().values().filter(|item| !item.checked).count());
+
+    let filtered_todos = use_memo(move || {
+        let mut filtered_todos = todos
+            .read()
+            .iter()
+            .filter(|(_, item)| match filter() {
+                FilterState::All => true,
+                FilterState::Active => !item.checked,
+                FilterState::Completed => item.checked,
+            })
+            .map(|f| *f.0)
+            .collect::<Vec<_>>();
 
-    let show_clear_completed = todos.values().any(|todo| todo.checked);
+        filtered_todos.sort_unstable();
+
+        filtered_todos
+    });
+
+    let toggle_all = move |_| {
+        let check = active_todo_count() != 0;
+        for (_, item) in todos.write().iter_mut() {
+            item.checked = check;
+        }
+    };
 
-    cx.render(rsx! {
+    rsx! {
         section { class: "todoapp",
-            style { {include_str!("./assets/todomvc.css")} }
-            TodoHeader { todos: todos }
+            style { {STYLE} }
+            TodoHeader { todos }
             section { class: "main",
-                if !todos.is_empty() {
+                if !todos.read().is_empty() {
                     input {
                         id: "toggle-all",
                         class: "toggle-all",
                         r#type: "checkbox",
-                        onchange: move |_| {
-                            let check = active_todo_count != 0;
-                            for (_, item) in todos.make_mut().iter_mut() {
-                                item.checked = check;
-                            }
-                        },
-                        checked: if active_todo_count == 0 { "true" } else { "false" },
+                        onchange: toggle_all,
+                        checked: active_todo_count() == 0,
                     }
                     label { r#for: "toggle-all" }
                 }
                 ul { class: "todo-list",
-                    for id in filtered_todos.iter() {
-                        TodoEntry {
-                            key: "{id}",
-                            id: *id,
-                            todos: todos,
-                        }
+                    for id in filtered_todos() {
+                        TodoEntry { key: "{id}", id, todos }
                     }
                 }
-                if !todos.is_empty() {
-                    ListFooter {
-                        active_todo_count: active_todo_count,
-                        active_todo_text: active_todo_text,
-                        show_clear_completed: show_clear_completed,
-                        todos: todos,
-                        filter: filter,
-                    }
+                if !todos.read().is_empty() {
+                    ListFooter { active_todo_count, todos, filter }
                 }
             }
         }
         PageFooter {}
-    })
-}
-
-#[derive(Props)]
-pub struct TodoHeaderProps<'a> {
-    todos: &'a UseState<im_rc::HashMap<u32, TodoItem>>,
+    }
 }
 
-pub fn TodoHeader<'a>(cx: Scope<'a, TodoHeaderProps<'a>>) -> Element {
-    let draft = use_state(cx, || "".to_string());
-    let todo_id = use_state(cx, || 0);
+#[component]
+fn TodoHeader(mut todos: Signal<HashMap<u32, TodoItem>>) -> Element {
+    let mut draft = use_signal(|| "".to_string());
+    let mut todo_id = use_signal(|| 0);
+
+    let onkeydown = move |evt: KeyboardEvent| {
+        if evt.key() == Key::Enter && !draft.read().is_empty() {
+            let id = todo_id();
+            let todo = TodoItem {
+                id,
+                checked: false,
+                contents: draft.to_string(),
+            };
+            todos.write().insert(id, todo);
+            todo_id += 1;
+            draft.set("".to_string());
+        }
+    };
 
-    cx.render(rsx! {
+    rsx! {
         header { class: "header",
             h1 { "todos" }
             input {
@@ -106,76 +110,46 @@ pub fn TodoHeader<'a>(cx: Scope<'a, TodoHeaderProps<'a>>) -> Element {
                 placeholder: "What needs to be done?",
                 value: "{draft}",
                 autofocus: "true",
-                oninput: move |evt| {
-                    draft.set(evt.value().clone());
-                },
-                onkeydown: move |evt| {
-                    if evt.key() == Key::Enter && !draft.is_empty() {
-                        cx.props
-                            .todos
-                            .make_mut()
-                            .insert(
-                                **todo_id,
-                                TodoItem {
-                                    id: **todo_id,
-                                    checked: false,
-                                    contents: draft.to_string(),
-                                },
-                            );
-                        *todo_id.make_mut() += 1;
-                        draft.set("".to_string());
-                    }
-                }
+                oninput: move |evt| draft.set(evt.value().clone()),
+                onkeydown,
             }
         }
-    })
-}
-
-#[derive(Props)]
-pub struct TodoEntryProps<'a> {
-    todos: &'a UseState<im_rc::HashMap<u32, TodoItem>>,
-    id: u32,
+    }
 }
 
-pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
-    let is_editing = use_state(cx, || false);
+#[component]
+fn TodoEntry(mut todos: Signal<HashMap<u32, TodoItem>>, id: u32) -> Element {
+    let mut is_editing = use_signal(|| false);
+    let checked = use_memo(move || todos.read().get(&id).unwrap().checked);
+    let contents = use_memo(move || todos.read().get(&id).unwrap().contents.clone());
 
-    let todos = cx.props.todos.get();
-    let todo = &todos[&cx.props.id];
-    let completed = if todo.checked { "completed" } else { "" };
-    let editing = if **is_editing { "editing" } else { "" };
-
-    cx.render(rsx!{
-        li { class: "{completed} {editing}",
+    rsx! {
+        li { class: if checked() { "completed" }, class: if is_editing() { "editing" },
             div { class: "view",
                 input {
                     class: "toggle",
                     r#type: "checkbox",
-                    id: "cbg-{todo.id}",
-                    checked: "{todo.checked}",
-                    oninput: move |evt| {
-                        cx.props.todos.make_mut()[&cx.props.id].checked = evt.value().parse().unwrap();
-                    }
+                    id: "cbg-{id}",
+                    checked: "{checked}",
+                    oninput: move |evt| todos.write().get_mut(&id).unwrap().checked = evt.value().parse().unwrap(),
                 }
                 label {
-                    r#for: "cbg-{todo.id}",
+                    r#for: "cbg-{id}",
                     ondoubleclick: move |_| is_editing.set(true),
                     prevent_default: "onclick",
-                    "{todo.contents}"
+                    "{contents}"
                 }
                 button {
                     class: "destroy",
-                    onclick: move |_| {
-                        cx.props.todos.make_mut().remove(&todo.id);
-                    },
+                    onclick: move |_| { todos.write().remove(&id); },
                     prevent_default: "onclick"
                 }
             }
-            if **is_editing {
+            if is_editing() {
                 input {
                     class: "edit",
-                    value: "{todo.contents}",
-                    oninput: move |evt| cx.props.todos.make_mut()[&cx.props.id].contents = evt.value(),
+                    value: "{contents}",
+                    oninput: move |evt| todos.write().get_mut(&id).unwrap().contents = evt.value(),
                     autofocus: "true",
                     onfocusout: move |_| is_editing.set(false),
                     onkeydown: move |evt| {
@@ -183,39 +157,32 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
                             Key::Enter | Key::Escape | Key::Tab => is_editing.set(false),
                             _ => {}
                         }
-                    },
+                    }
                 }
             }
         }
-    })
+    }
 }
 
-#[derive(Props)]
-pub struct ListFooterProps<'a> {
-    todos: &'a UseState<im_rc::HashMap<u32, TodoItem>>,
-    active_todo_count: usize,
-    active_todo_text: &'a str,
-    show_clear_completed: bool,
-    filter: &'a UseState<FilterState>,
-}
-
-pub fn ListFooter<'a>(cx: Scope<'a, ListFooterProps<'a>>) -> Element {
-    let active_todo_count = cx.props.active_todo_count;
-    let active_todo_text = cx.props.active_todo_text;
+#[component]
+fn ListFooter(
+    mut todos: Signal<HashMap<u32, TodoItem>>,
+    active_todo_count: ReadOnlySignal<usize>,
+    mut filter: Signal<FilterState>,
+) -> Element {
+    let show_clear_completed = use_memo(move || todos.read().values().any(|todo| todo.checked));
 
-    let selected = |state| {
-        if *cx.props.filter == state {
-            "selected"
-        } else {
-            "false"
-        }
-    };
-
-    cx.render(rsx! {
+    rsx! {
         footer { class: "footer",
             span { class: "todo-count",
                 strong { "{active_todo_count} " }
-                span { "{active_todo_text} left" }
+                span {
+                    match active_todo_count() {
+                        1 => "item",
+                        _ => "items",
+                    }
+                    " left"
+                }
             }
             ul { class: "filters",
                 for (state , state_text , url) in [
@@ -226,27 +193,27 @@ pub fn ListFooter<'a>(cx: Scope<'a, ListFooterProps<'a>>) -> Element {
                     li {
                         a {
                             href: url,
-                            class: selected(state),
-                            onclick: move |_| cx.props.filter.set(state),
+                            class: if filter() == state { "selected" },
+                            onclick: move |_| filter.set(state),
                             prevent_default: "onclick",
                             {state_text}
                         }
                     }
                 }
             }
-            if cx.props.show_clear_completed {
+            if show_clear_completed() {
                 button {
                     class: "clear-completed",
-                    onclick: move |_| cx.props.todos.make_mut().retain(|_, todo| !todo.checked),
+                    onclick: move |_| todos.write().retain(|_, todo| !todo.checked),
                     "Clear completed"
                 }
             }
         }
-    })
+    }
 }
 
-pub fn PageFooter(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn PageFooter() -> Element {
+    rsx! {
         footer { class: "info",
             p { "Double-click to edit a todo" }
             p {
@@ -258,5 +225,5 @@ pub fn PageFooter(cx: Scope) -> Element {
                 a { href: "http://todomvc.com", "TodoMVC" }
             }
         }
-    })
+    }
 }

+ 7 - 7
examples/video_stream.rs

@@ -1,7 +1,7 @@
+use dioxus::desktop::wry::http;
+use dioxus::desktop::wry::http::Response;
+use dioxus::desktop::{use_asset_handler, AssetRequest};
 use dioxus::prelude::*;
-use dioxus_desktop::wry::http;
-use dioxus_desktop::wry::http::Response;
-use dioxus_desktop::{use_asset_handler, AssetRequest};
 use http::{header::*, response::Builder as ResponseBuilder, status::StatusCode};
 use std::{io::SeekFrom, path::PathBuf};
 use tokio::io::AsyncReadExt;
@@ -26,11 +26,11 @@ fn main() {
                 }
             });
     }
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    use_asset_handler(cx, "videos", move |request, responder| {
+fn app() -> Element {
+    use_asset_handler("videos", move |request, responder| {
         // Using dioxus::spawn works, but is slower than a dedicated thread
         tokio::task::spawn(async move {
             let video_file = PathBuf::from(VIDEO_PATH);
@@ -43,7 +43,7 @@ fn app(cx: Scope) -> Element {
         });
     });
 
-    render! {
+    rsx! {
         div {
             video {
                 src: "/videos/test_video.mp4",

+ 4 - 4
examples/web_component.rs

@@ -1,13 +1,13 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         web-component {
             "my-prop": "5%",
         }
-    })
+    }
 }

+ 35 - 40
examples/window_event.rs

@@ -1,75 +1,70 @@
+use dioxus::desktop::{window, Config, WindowBuilder};
 use dioxus::prelude::*;
-use dioxus_desktop::{Config, WindowBuilder};
 
 fn main() {
-    let cfg = Config::new().with_window(
-        WindowBuilder::new()
-            .with_title("Borderless Window")
-            .with_decorations(false),
-    );
-
-    dioxus_desktop::launch_cfg(app, cfg);
+    LaunchBuilder::desktop()
+        .with_cfg(
+            Config::new().with_window(
+                WindowBuilder::new()
+                    .with_title("Borderless Window")
+                    .with_decorations(false),
+            ),
+        )
+        .launch(app)
 }
 
-fn app(cx: Scope) -> Element {
-    let window = dioxus_desktop::use_window(cx);
-
-    // if you want to make window fullscreen, you need close the resizable.
-    // window.set_fullscreen(true);
-    // window.set_resizable(false);
-
-    let fullscreen = use_state(cx, || false);
-    let always_on_top = use_state(cx, || false);
-    let decorations = use_state(cx, || false);
+fn app() -> Element {
+    let mut fullscreen = use_signal(|| false);
+    let mut always_on_top = use_signal(|| false);
+    let mut decorations = use_signal(|| false);
 
-    cx.render(rsx!(
-        link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" }
+    rsx!(
+        link {
+            href: "https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css",
+            rel: "stylesheet"
+        }
         header {
             class: "text-gray-400 bg-gray-900 body-font",
-            onmousedown: move |_| window.drag(),
-            div {
-                class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center",
+            onmousedown: move |_| window().drag(),
+            div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center",
                 a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0",
-                    span { class: "ml-3 text-xl", "Dioxus"}
+                    span { class: "ml-3 text-xl", "Dioxus" }
                 }
                 nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center" }
                 button {
                     class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
                     onmousedown: |evt| evt.stop_propagation(),
-                    onclick: move |_| window.set_minimized(true),
+                    onclick: move |_| window().set_minimized(true),
                     "Minimize"
                 }
                 button {
                     class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
                     onmousedown: |evt| evt.stop_propagation(),
                     onclick: move |_| {
-
-                        window.set_fullscreen(!**fullscreen);
-                        window.set_resizable(**fullscreen);
-                        fullscreen.modify(|f| !*f);
+                        window().set_fullscreen(!fullscreen());
+                        window().set_resizable(fullscreen());
+                        fullscreen.toggle();
                     },
                     "Fullscreen"
                 }
                 button {
                     class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
                     onmousedown: |evt| evt.stop_propagation(),
-                    onclick: move |_| window.close(),
+                    onclick: move |_| window().close(),
                     "Close"
                 }
             }
         }
         br {}
-        div {
-            class: "container mx-auto",
-            div {
-                class: "grid grid-cols-5",
+        div { class: "container mx-auto",
+            div { class: "grid grid-cols-5",
                 div {
                     button {
                         class: "inline-flex items-center text-white bg-green-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
                         onmousedown: |evt| evt.stop_propagation(),
                         onclick: move |_| {
-                            window.set_always_on_top(!always_on_top);
-                            always_on_top.set(!always_on_top);
+                            window().set_always_on_top(!always_on_top());
+                            always_on_top.toggle();
                         },
                         "Always On Top"
                     }
@@ -79,8 +74,8 @@ fn app(cx: Scope) -> Element {
                         class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
                         onmousedown: |evt| evt.stop_propagation(),
                         onclick: move |_| {
-                            window.set_decorations(!decorations);
-                            decorations.set(!decorations);
+                            window().set_decorations(!decorations());
+                            decorations.toggle();
                         },
                         "Set Decorations"
                     }
@@ -89,11 +84,11 @@ fn app(cx: Scope) -> Element {
                     button {
                         class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
                         onmousedown: |evt| evt.stop_propagation(),
-                        onclick: move |_| window.set_title("Dioxus Application"),
+                        onclick: move |_| window().set_title("Dioxus Application"),
                         "Change Title"
                     }
                 }
             }
         }
-    ))
+    )
 }

+ 23 - 33
examples/window_focus.rs

@@ -1,45 +1,35 @@
+use dioxus::desktop::tao::event::Event as WryEvent;
+use dioxus::desktop::tao::event::WindowEvent;
+use dioxus::desktop::use_wry_event_handler;
+use dioxus::desktop::{Config, WindowCloseBehaviour};
 use dioxus::prelude::*;
-use dioxus_desktop::tao::event::Event as WryEvent;
-use dioxus_desktop::tao::event::WindowEvent;
-use dioxus_desktop::use_wry_event_handler;
-use dioxus_desktop::{Config, WindowCloseBehaviour};
 
 fn main() {
-    let cfg = Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow);
-
-    dioxus_desktop::launch_cfg(app, cfg);
+    LaunchBuilder::desktop()
+        .with_cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow))
+        .launch(app)
 }
 
-fn app(cx: Scope) -> Element {
-    let focused = use_state(cx, || false);
+fn app() -> Element {
+    let mut focused = use_signal(|| true);
 
-    use_wry_event_handler(cx, {
-        to_owned![focused];
-        move |event, _| {
-            if let WryEvent::WindowEvent {
-                event: WindowEvent::Focused(new_focused),
-                ..
-            } = event
-            {
-                focused.set(*new_focused);
-            }
+    use_wry_event_handler(move |event, _| {
+        if let WryEvent::WindowEvent {
+            event: WindowEvent::Focused(new_focused),
+            ..
+        } = event
+        {
+            focused.set(*new_focused)
         }
     });
 
-    cx.render(rsx! {
-        div{
-            width: "100%",
-            height: "100%",
-            display: "flex",
-            flex_direction: "column",
-            align_items: "center",
-            {
-                if *focused.get() {
-                    "This window is focused!"
-                } else {
-                    "This window is not focused!"
-                }
+    rsx! {
+        div { width: "100%", height: "100%", display: "flex", flex_direction: "column", align_items: "center",
+            if focused() {
+                "This window is focused!"
+            } else {
+                "This window is not focused!"
             }
         }
-    })
+    }
 }

+ 7 - 7
examples/window_zoom.rs

@@ -1,22 +1,22 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let level = use_state(cx, || 1.0);
+fn app() -> Element {
+    let mut level = use_signal(|| 1.0);
 
-    cx.render(rsx! {
+    rsx! {
         input {
             r#type: "number",
             value: "{level}",
-            oninput: |e| {
+            oninput: move |e| {
                 if let Ok(new_zoom) = e.value().parse::<f64>() {
                     level.set(new_zoom);
-                    dioxus_desktop::window().webview.zoom(new_zoom);
+                    dioxus::desktop::window().webview.zoom(new_zoom);
                 }
             }
         }
-    })
+    }
 }

+ 5 - 7
examples/xss_safety.rs

@@ -5,15 +5,13 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch_desktop(app);
 }
 
-fn app(cx: Scope) -> Element {
-    let contents = use_state(cx, || {
-        String::from("<script>alert(\"hello world\")</script>")
-    });
+fn app() -> Element {
+    let mut contents = use_signal(|| String::from("<script>alert(\"hello world\")</script>"));
 
-    cx.render(rsx! {
+    rsx! {
         div {
             h1 {"Dioxus is XSS-Safe"}
             h3 { "{contents}" }
@@ -23,5 +21,5 @@ fn app(cx: Scope) -> Element {
                 oninput: move |e| contents.set(e.value()),
             }
         }
-    })
+    }
 }

+ 1 - 1
packages/autofmt/tests/samples/immediate_expr.rsx

@@ -1,4 +1,4 @@
 fn it_works() {
-    cx.render(rsx!({()}))
+    rsx!({()})
 }
 

+ 4 - 4
packages/autofmt/tests/samples/long.rsx

@@ -5,8 +5,8 @@ pub fn Explainer<'a>(
     cx: Scope<'a>,
     invert: bool,
     title: &'static str,
-    content: Element<'a>,
-    flasher: Element<'a>,
+    content: Element,
+    flasher: Element,
 ) -> Element {
     // pt-5 sm:pt-24 lg:pt-24
 
@@ -32,10 +32,10 @@ pub fn Explainer<'a>(
         std::mem::swap(&mut left, &mut right);
     }
 
-    cx.render(rsx! {
+    rsx! {
         div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light",
             {left},
             {right}
         }
-    })
+    }
 }

+ 3 - 3
packages/autofmt/tests/samples/messy_indent.rsx

@@ -1,4 +1,4 @@
-fn SaveClipboard(cx: Scope) -> Element {
+fn SaveClipboard() -> Element {
     rsx! {
         div { class: "relative w-1/2 {align} max-w-md leading-8",
             h2 { class: "mb-6 text-3xl leading-tight md:text-4xl md:leading-tight lg:text-3xl lg:leading-tight font-heading font-mono font-bold",
@@ -7,7 +7,7 @@ fn SaveClipboard(cx: Scope) -> Element {
         }
     };
 
-    cx.render(rsx! {
+    rsx! {
         div { "hello world", "hello world", "hello world" }
-    })
+    }
 }

+ 2 - 2
packages/autofmt/tests/samples/reallylong.rsx

@@ -1,5 +1,5 @@
 pub static Icon3: Component<()> = |cx| {
-    cx.render(rsx! {
+    rsx! {
         svg {
             class: "w-6 h-6",
             stroke_linecap: "round",
@@ -11,5 +11,5 @@ pub static Icon3: Component<()> = |cx| {
             path { d: "M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" }
             circle { cx: "12", cy: "7", r: "4" }
         }
-    })
+    }
 };

+ 2 - 2
packages/autofmt/tests/samples/trailing_expr.rsx

@@ -1,7 +1,7 @@
 fn it_works() {
-    cx.render(rsx! {
+    rsx! {
         div {
             span { "Description: ", {package.description.as_deref().unwrap_or("❌❌❌❌ missing")} }
         }
-    })
+    }
 }

+ 2 - 2
packages/autofmt/tests/wrong/multi-4sp.rsx

@@ -1,3 +1,3 @@
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! { div { "hello world" } })
+fn app() -> Element {
+    rsx! { div { "hello world" } }
 }

+ 3 - 3
packages/autofmt/tests/wrong/multi-4sp.wrong.rsx

@@ -1,5 +1,5 @@
-fn app(cx: Scope) -> Element {
-    cx.render(rsx! {
+fn app() -> Element {
+    rsx! {
         div {"hello world" }
-    })
+    }
 }

+ 2 - 2
packages/autofmt/tests/wrong/multi-tab.rsx

@@ -1,3 +1,3 @@
-fn app(cx: Scope) -> Element {
-	cx.render(rsx! { div { "hello world" } })
+fn app() -> Element {
+	rsx! { div { "hello world" } }
 }

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません