Bläddra i källkod

Merge pull request #32 from DioxusLabs/jk/reworking_components

wip: rework components, safety, and tasks
Jonathan Kelley 3 år sedan
förälder
incheckning
0e6c845690
58 ändrade filer med 4187 tillägg och 3809 borttagningar
  1. 1 1
      examples/borrowed.rs
  2. 13 11
      examples/crm.rs
  3. 9 5
      examples/rsx_usage.rs
  4. 1 1
      examples/ssr.rs
  5. 6 4
      examples/weather_app.rs
  6. 2 2
      packages/core-macro/src/rsx/element.rs
  7. 1 3
      packages/core/Cargo.toml
  8. 16 0
      packages/core/README.md
  9. 102 0
      packages/core/architecture.md
  10. 2 2
      packages/core/benches/jsframework.rs
  11. 9 0
      packages/core/examples/props_expand.rs
  12. 44 0
      packages/core/examples/works.rs
  13. 6 41
      packages/core/src/component.rs
  14. 0 17
      packages/core/src/coroutines.rs
  15. 394 268
      packages/core/src/diff.rs
  16. 0 4
      packages/core/src/diff_stack.rs
  17. 0 50
      packages/core/src/heuristics.rs
  18. 20 15
      packages/core/src/hooklist.rs
  19. 165 73
      packages/core/src/lazynodes.rs
  20. 11 30
      packages/core/src/lib.rs
  21. 115 43
      packages/core/src/nodes.rs
  22. 27 45
      packages/core/src/old/bumpframe.rs
  23. 1 1
      packages/core/src/old/childiter.rs
  24. 6 3
      packages/core/src/old/debug_dom.rs
  25. 15 0
      packages/core/src/old/events.rs
  26. 74 74
      packages/core/src/old/hooks.rs
  27. 0 0
      packages/core/src/old/noderef.rs
  28. 3 3
      packages/core/src/old/resources.rs
  29. 105 121
      packages/core/src/old/scheduler.rs
  30. 0 0
      packages/core/src/old/threadsafe.rs
  31. 220 329
      packages/core/src/scope.rs
  32. 290 0
      packages/core/src/scopearena.rs
  33. 0 39
      packages/core/src/tasks.rs
  34. 26 15
      packages/core/src/test_dom.rs
  35. 0 57
      packages/core/src/util.rs
  36. 361 164
      packages/core/src/virtual_dom.rs
  37. 4 4
      packages/core/tests/borrowedstate.rs
  38. 22 21
      packages/core/tests/create_dom.rs
  39. 1 1
      packages/core/tests/diffing.rs
  40. 2 4
      packages/core/tests/display_vdom.rs
  41. 2 2
      packages/core/tests/lifecycle.rs
  42. 2 2
      packages/core/tests/sharedstate.rs
  43. 15 14
      packages/core/tests/vdom_rebuild.rs
  44. 1 1
      packages/desktop/Cargo.toml
  45. 1 4
      packages/desktop/src/desktop_context.rs
  46. 1 0
      packages/desktop/src/err.rs
  47. 6 6
      packages/desktop/src/events.rs
  48. 0 1
      packages/desktop/src/lib.rs
  49. 9 8
      packages/hooks/src/use_shared_state.rs
  50. 0 1
      packages/hooks/src/useref.rs
  51. 0 1
      packages/hooks/src/usestate.rs
  52. 8 3
      packages/html/Cargo.toml
  53. 0 33
      packages/html/src/attrval.rs
  54. 1126 0
      packages/html/src/elements.rs
  55. 5 91
      packages/html/src/events.rs
  56. 925 0
      packages/html/src/global_attributes.rs
  57. 7 2183
      packages/html/src/lib.rs
  58. 5 8
      src/lib.rs

+ 1 - 1
examples/borrowed.rs

@@ -21,7 +21,7 @@ fn main() {
 }
 }
 
 
 fn App((cx, props): Scope<()>) -> Element {
 fn App((cx, props): Scope<()>) -> Element {
-    let text: &mut Vec<String> = cx.use_hook(|_| vec![String::from("abc=def")], |f| f, |_| {});
+    let text: &mut Vec<String> = cx.use_hook(|_| vec![String::from("abc=def")], |f| f);
 
 
     let first = text.get_mut(0).unwrap();
     let first = text.get_mut(0).unwrap();
 
 

+ 13 - 11
examples/crm.rs

@@ -90,15 +90,17 @@ static App: FC<()> = |(cx, _)| {
         }
         }
     };
     };
 
 
-    rsx!(cx, body {
-        link {
-            rel: "stylesheet"
-            href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
-            integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
-            crossorigin: "anonymous"
-        }
-        margin_left: "35%"
-        h1 {"Dioxus CRM Example"}
-        {scene}
-    })
+    cx.render(rsx!(
+        body {
+           link {
+               rel: "stylesheet"
+               href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
+               integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
+               crossorigin: "anonymous"
+           }
+           margin_left: "35%"
+           h1 {"Dioxus CRM Example"}
+           {scene}
+       }
+    ))
 };
 };

+ 9 - 5
examples/rsx_usage.rs

@@ -102,12 +102,9 @@ pub static Example: FC<()> = |(cx, props)| {
             }}
             }}
 
 
             // Matching
             // Matching
-            // Matching will throw a Rust error about "no two closures are the same type"
-            // To fix this, call "render" method or use the "in" syntax to produce VNodes.
-            // There's nothing we can do about it, sorry :/ (unless you want *really* unhygienic macros)
             {match true {
             {match true {
-                true => rsx!(cx, h1 {"Top text"}),
-                false => cx.render(rsx!( h1 {"Bottom text"}))
+                true => rsx!( h1 {"Top text"}),
+                false => rsx!( h1 {"Bottom text"})
             }}
             }}
 
 
             // Conditional rendering
             // Conditional rendering
@@ -174,10 +171,17 @@ pub static Example: FC<()> = |(cx, props)| {
 
 
             // Can take children too!
             // Can take children too!
             Taller { a: "asd", div {"hello world!"} }
             Taller { a: "asd", div {"hello world!"} }
+
+            // helper functions
+            {helper(cx, "hello world!")}
         }
         }
     })
     })
 };
 };
 
 
+fn helper(cx: Context, text: &str) -> Element {
+    rsx!(cx, p { "{text}" })
+}
+
 mod baller {
 mod baller {
     use super::*;
     use super::*;
     #[derive(Props, PartialEq)]
     #[derive(Props, PartialEq)]

+ 1 - 1
examples/ssr.rs

@@ -21,6 +21,6 @@ static App: FC<()> = |(cx, props)| {
 struct MyProps<'a> {
 struct MyProps<'a> {
     text: &'a str,
     text: &'a str,
 }
 }
-fn App2<'a>(cx: Context<'a>, props: &'a MyProps) -> Element<'a> {
+fn App2(cx: Context, props: &MyProps) -> Element {
     None
     None
 }
 }

+ 6 - 4
examples/weather_app.rs

@@ -24,14 +24,16 @@ static App: FC<()> = |(cx, props)| {
                 .await
                 .await
                 .unwrap();
                 .unwrap();
         },
         },
-        |cx, props| {
+        |props| {
             //
             //
-            rsx!(WeatherDisplay {})
+            cx.render(rsx!(WeatherDisplay {}))
         },
         },
     );
     );
 
 
-    rsx!(cx, div {
-        {body}
+    cx.render(rsx! {
+        div {
+            {body}
+        }
     })
     })
 };
 };
 
 

+ 2 - 2
packages/core-macro/src/rsx/element.rs

@@ -210,12 +210,12 @@ impl ToTokens for ElementAttrNamed {
 
 
             ElementAttr::EventClosure { name, closure } => {
             ElementAttr::EventClosure { name, closure } => {
                 quote! {
                 quote! {
-                    dioxus::events::on::#name(__cx, #closure)
+                    dioxus_elements::on::#name(__cx, #closure)
                 }
                 }
             }
             }
             ElementAttr::EventTokens { name, tokens } => {
             ElementAttr::EventTokens { name, tokens } => {
                 quote! {
                 quote! {
-                    dioxus::events::on::#name(__cx, #tokens)
+                    dioxus_elements::on::#name(__cx, #tokens)
                 }
                 }
             }
             }
         };
         };

+ 1 - 3
packages/core/Cargo.toml

@@ -40,8 +40,6 @@ indexmap = "1.7.0"
 # Serialize the Edits for use in Webview/Liveview instances
 # Serialize the Edits for use in Webview/Liveview instances
 serde = { version = "1", features = ["derive"], optional = true }
 serde = { version = "1", features = ["derive"], optional = true }
 
 
-serde_repr = { version = "0.1.7", optional = true }
-
 [dev-dependencies]
 [dev-dependencies]
 anyhow = "1.0.42"
 anyhow = "1.0.42"
 dioxus-html = { path = "../html" }
 dioxus-html = { path = "../html" }
@@ -54,7 +52,7 @@ dioxus-core-macro = { path = "../core-macro", version = "0.1.2" }
 
 
 [features]
 [features]
 default = []
 default = []
-serialize = ["serde", "serde_repr"]
+serialize = ["serde"]
 debug_vdom = []
 debug_vdom = []
 
 
 [[bench]]
 [[bench]]

+ 16 - 0
packages/core/README.md

@@ -40,3 +40,19 @@ We have big goals for Dioxus. The final implementation must:
 - Be "live". Components should be able to be both server rendered and client rendered without needing frontend APIs.
 - Be "live". Components should be able to be both server rendered and client rendered without needing frontend APIs.
 - Be modular. Components and hooks should be work anywhere without worrying about target platform.
 - Be modular. Components and hooks should be work anywhere without worrying about target platform.
 
 
+
+
+## Safety
+
+Dioxus deals with arenas, lifetimes, asynchronous tasks, custom allocators, pinning, and a lot more foundational low-level work that is very difficult to implement with 0 unsafe.
+
+If you don't want to use a crate that uses unsafe, then this crate is not for you.
+
+however, we are always interested in decreasing the scope of the core VirtualDom to make it easier to review.
+
+We'd also be happy to welcome PRs that can eliminate unsafe code while still upholding the numerous variants required to execute certain features.
+
+There's a few invariants that are very important:
+
+- References to `ScopeInner` and `Props` passed into components are *always* valid for as long as the component exists. Even if the scope backing is resized to fit more scopes, the scope has to stay the same place in memory.
+

+ 102 - 0
packages/core/architecture.md

@@ -99,3 +99,105 @@ Internally, the flow of suspense works like this:
 9. diff that node with the new node with a low priority on its own fiber
 9. diff that node with the new node with a low priority on its own fiber
 10. return the patches back to the event loop
 10. return the patches back to the event loop
 11. apply the patches to the real dom
 11. apply the patches to the real dom
+
+/*
+Welcome to Dioxus's cooperative, priority-based scheduler.
+
+I hope you enjoy your stay.
+
+Some essential reading:
+- https://github.com/facebook/react/blob/main/packages/scheduler/src/forks/Scheduler.js#L197-L200
+- https://github.com/facebook/react/blob/main/packages/scheduler/src/forks/Scheduler.js#L440
+- https://github.com/WICG/is-input-pending
+- https://web.dev/rail/
+- https://indepth.dev/posts/1008/inside-fiber-in-depth-overview-of-the-new-reconciliation-algorithm-in-react
+
+# What's going on?
+
+Dioxus is a framework for "user experience" - not just "user interfaces." Part of the "experience" is keeping the UI
+snappy and "jank free" even under heavy work loads. Dioxus already has the "speed" part figured out - but there's no
+point in being "fast" if you can't also be "responsive."
+
+As such, Dioxus can manually decide on what work is most important at any given moment in time. With a properly tuned
+priority system, Dioxus can ensure that user interaction is prioritized and committed as soon as possible (sub 100ms).
+The controller responsible for this priority management is called the "scheduler" and is responsible for juggling many
+different types of work simultaneously.
+
+# How does it work?
+
+Per the RAIL guide, we want to make sure that A) inputs are handled ASAP and B) animations are not blocked.
+React-three-fiber is a testament to how amazing this can be - a ThreeJS scene is threaded in between work periods of
+React, and the UI still stays snappy!
+
+While it's straightforward to run code ASAP and be as "fast as possible", what's not  _not_ straightforward is how to do
+this while not blocking the main thread. The current prevailing thought is to stop working periodically so the browser
+has time to paint and run animations. When the browser is finished, we can step in and continue our work.
+
+React-Fiber uses the "Fiber" concept to achieve a pause-resume functionality. This is worth reading up on, but not
+necessary to understand what we're doing here. In Dioxus, our DiffMachine is guided by DiffInstructions - essentially
+"commands" that guide the Diffing algorithm through the tree. Our "diff_scope" method is async - we can literally pause
+our DiffMachine "mid-sentence" (so to speak) by just stopping the poll on the future. The DiffMachine periodically yields
+so Rust's async machinery can take over, allowing us to customize when exactly to pause it.
+
+React's "should_yield" method is more complex than ours, and I assume we'll move in that direction as Dioxus matures. For
+now, Dioxus just assumes a TimeoutFuture, and selects! on both the Diff algorithm and timeout. If the DiffMachine finishes
+before the timeout, then Dioxus will work on any pending work in the interim. If there is no pending work, then the changes
+are committed, and coroutines are polled during the idle period. However, if the timeout expires, then the DiffMachine
+future is paused and saved (self-referentially).
+
+# Priority System
+
+So far, we've been able to thread our Dioxus work between animation frames - the main thread is not blocked! But that
+doesn't help us _under load_. How do we still stay snappy... even if we're doing a lot of work? Well, that's where
+priorities come into play. The goal with priorities is to schedule shorter work as a "high" priority and longer work as
+a "lower" priority. That way, we can interrupt long-running low-priority work with short-running high-priority work.
+
+React's priority system is quite complex.
+
+There are 5 levels of priority and 2 distinctions between UI events (discrete, continuous). I believe React really only
+uses 3 priority levels and "idle" priority isn't used... Regardless, there's some batching going on.
+
+For Dioxus, we're going with a 4 tier priority system:
+- Sync: Things that need to be done by the next frame, like TextInput on controlled elements
+- High: for events that block all others - clicks, keyboard, and hovers
+- Medium: for UI events caused by the user but not directly - scrolls/forms/focus (all other events)
+- Low: set_state called asynchronously, and anything generated by suspense
+
+In "Sync" state, we abort our "idle wait" future, and resolve the sync queue immediately and escape. Because we completed
+work before the next rAF, any edits can be immediately processed before the frame ends. Generally though, we want to leave
+as much time to rAF as possible. "Sync" is currently only used by onInput - we'll leave some docs telling people not to
+do anything too arduous from onInput.
+
+For the rest, we defer to the rIC period and work down each queue from high to low.
+*/
+
+
+
+Strategy:
+- When called, check for any UI events that might've been received since the last frame.
+- Dump all UI events into a "pending discrete" queue and a "pending continuous" queue.
+
+- If there are any pending discrete events, then elevate our priority level. If our priority level is already "high,"
+    then we need to finish the high priority work first. If the current work is "low" then analyze what scopes
+    will be invalidated by this new work. If this interferes with any in-flight medium or low work, then we need
+    to bump the other work out of the way, or choose to process it so we don't have any conflicts.
+    'static components have a leg up here since their work can be re-used among multiple scopes.
+    "High priority" is only for blocking! Should only be used on "clicks"
+
+- If there are no pending discrete events, then check for continuous events. These can be completely batched
+
+- we batch completely until we run into a discrete event
+- all continuous events are batched together
+- so D C C C C C would be two separate events - D and C. IE onclick and onscroll
+- D C C C C C C D C C C D would be D C D C D in 5 distinct phases.
+
+- !listener bubbling is not currently implemented properly and will need to be implemented somehow in the future
+    - we need to keep track of element parents to be able to traverse properly
+
+
+Open questions:
+- what if we get two clicks from the component during the same slice?
+    - should we batch?
+    - react says no - they are continuous
+    - but if we received both - then we don't need to diff, do we? run as many as we can and then finally diff?
+

+ 2 - 2
packages/core/benches/jsframework.rs

@@ -24,7 +24,7 @@ criterion_group!(mbenches, create_rows);
 criterion_main!(mbenches);
 criterion_main!(mbenches);
 
 
 fn create_rows(c: &mut Criterion) {
 fn create_rows(c: &mut Criterion) {
-    static App: FC<()> = |(cx, _)| {
+    static App: FC<()> = |cx, _| {
         let mut rng = SmallRng::from_entropy();
         let mut rng = SmallRng::from_entropy();
         let rows = (0..10_000_usize).map(|f| {
         let rows = (0..10_000_usize).map(|f| {
             let label = Label::new(&mut rng);
             let label = Label::new(&mut rng);
@@ -58,7 +58,7 @@ struct RowProps {
     row_id: usize,
     row_id: usize,
     label: Label,
     label: Label,
 }
 }
-fn Row((cx, props): Scope<RowProps>) -> Element {
+fn Row(cx: Context, props: &RowProps) -> Element {
     let [adj, col, noun] = props.label.0;
     let [adj, col, noun] = props.label.0;
     cx.render(rsx! {
     cx.render(rsx! {
         tr {
         tr {

+ 9 - 0
packages/core/examples/props_expand.rs

@@ -0,0 +1,9 @@
+use dioxus_core as dioxus;
+use dioxus_core_macro::*;
+
+fn main() {}
+
+#[derive(Props)]
+struct ChildProps<'a> {
+    name: &'a str,
+}

+ 44 - 0
packages/core/examples/works.rs

@@ -0,0 +1,44 @@
+use dioxus::prelude::*;
+use dioxus_core as dioxus;
+use dioxus_core_macro::*;
+use dioxus_html as dioxus_elements;
+
+fn main() {
+    let _ = VirtualDom::new(Parent);
+}
+
+fn Parent(cx: Context, props: &()) -> Element {
+    let value = cx.use_hook(|_| String::new(), |f| &*f);
+
+    cx.render(rsx! {
+        div {
+            Child { name: value }
+            Fragment { "asd" }
+        }
+    })
+}
+
+#[derive(Props)]
+struct ChildProps<'a> {
+    name: &'a str,
+}
+
+fn Child(cx: Context, props: &ChildProps) -> Element {
+    cx.render(rsx! {
+        div {
+            h1 { "it's nested" }
+            Child2 { name: props.name }
+        }
+    })
+}
+
+#[derive(Props)]
+struct Grandchild<'a> {
+    name: &'a str,
+}
+
+fn Child2(cx: Context, props: &Grandchild) -> Element {
+    cx.render(rsx! {
+        div { "Hello {props.name}!" }
+    })
+}

+ 6 - 41
packages/core/src/component.rs

@@ -5,43 +5,7 @@
 //! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
 //! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
 //! that ensures compile-time required and optional fields on cx.
 //! that ensures compile-time required and optional fields on cx.
 
 
-use crate::{
-    innerlude::{Context, Element, VAnchor, VFragment, VNode},
-    LazyNodes, ScopeChildren,
-};
-/// A component is a wrapper around a Context and some Props that share a lifetime
-///
-///
-/// # Example
-///
-/// With memoized state:
-/// ```rust
-/// struct State {}
-///
-/// fn Example((cx, props): Scope<State>) -> DomTree {
-///     // ...
-/// }
-/// ```
-///
-/// With borrowed state:
-/// ```rust
-/// struct State<'a> {
-///     name: &'a str
-/// }
-///
-/// fn Example<'a>((cx, props): Scope<'a, State>) -> DomTree<'a> {
-///     // ...
-/// }
-/// ```
-///
-/// With owned state as a closure:
-/// ```rust
-/// static Example: FC<()> = |(cx, props)| {
-///     // ...
-/// };
-/// ```
-///
-pub type Scope<'a, T> = (Context<'a>, &'a T);
+use crate::innerlude::{Context, Element, LazyNodes, ScopeChildren};
 
 
 pub struct FragmentProps<'a> {
 pub struct FragmentProps<'a> {
     children: ScopeChildren<'a>,
     children: ScopeChildren<'a>,
@@ -51,7 +15,7 @@ pub struct FragmentBuilder<'a, const BUILT: bool> {
     children: Option<ScopeChildren<'a>>,
     children: Option<ScopeChildren<'a>>,
 }
 }
 impl<'a> FragmentBuilder<'a, false> {
 impl<'a> FragmentBuilder<'a, false> {
-    pub fn children(mut self, children: ScopeChildren<'a>) -> FragmentBuilder<'a, true> {
+    pub fn children(self, children: ScopeChildren<'a>) -> FragmentBuilder<'a, true> {
         FragmentBuilder {
         FragmentBuilder {
             children: Some(children),
             children: Some(children),
         }
         }
@@ -75,7 +39,7 @@ impl<'a> Properties for FragmentProps<'a> {
         FragmentBuilder { children: None }
         FragmentBuilder { children: None }
     }
     }
 
 
-    unsafe fn memoize(&self, other: &Self) -> bool {
+    unsafe fn memoize(&self, _other: &Self) -> bool {
         false
         false
     }
     }
 }
 }
@@ -102,7 +66,7 @@ impl<'a> Properties for FragmentProps<'a> {
 /// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
 /// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
 ///
 ///
 #[allow(non_upper_case_globals, non_snake_case)]
 #[allow(non_upper_case_globals, non_snake_case)]
-pub fn Fragment<'a>((cx, props): Scope<'a, FragmentProps<'a>>) -> Element<'a> {
+pub fn Fragment<'a>(cx: Context<'a>, props: &'a FragmentProps<'a>) -> Element {
     cx.render(Some(LazyNodes::new(|f| {
     cx.render(Some(LazyNodes::new(|f| {
         f.fragment_from_iter(&props.children)
         f.fragment_from_iter(&props.children)
     })))
     })))
@@ -173,6 +137,7 @@ impl EmptyBuilder {
 
 
 /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
 /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
 /// to initialize a component's props.
 /// to initialize a component's props.
-pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element<'a>) -> T::Builder {
+pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Context<'a>, &'a T) -> Element) -> T::Builder {
+    // pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
     T::builder()
     T::builder()
 }
 }

+ 0 - 17
packages/core/src/coroutines.rs

@@ -1,17 +0,0 @@
-//! Coroutines are just a "futures unordered" buffer for tasks that can be submitted through the use_coroutine hook.
-//!
-//! The idea here is to move *coroutine* support as a layer on top of *tasks*
-
-use futures_util::{stream::FuturesUnordered, Future};
-
-pub struct CoroutineScheduler {
-    futures: FuturesUnordered<Box<dyn Future<Output = ()>>>,
-}
-
-impl CoroutineScheduler {
-    pub fn new() -> Self {
-        CoroutineScheduler {
-            futures: FuturesUnordered::new(),
-        }
-    }
-}

+ 394 - 268
packages/core/src/diff.rs

@@ -90,6 +90,7 @@
 
 
 use crate::innerlude::*;
 use crate::innerlude::*;
 use fxhash::{FxHashMap, FxHashSet};
 use fxhash::{FxHashMap, FxHashSet};
+use slab::Slab;
 use DomEdit::*;
 use DomEdit::*;
 
 
 /// Our DiffMachine is an iterative tree differ.
 /// Our DiffMachine is an iterative tree differ.
@@ -104,76 +105,33 @@ use DomEdit::*;
 ///
 ///
 /// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's
 /// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's
 /// stack machines all the way down!
 /// stack machines all the way down!
-pub(crate) struct DiffMachine<'bump> {
-    pub vdom: &'bump ResourcePool,
+pub struct DiffState<'bump> {
     pub mutations: Mutations<'bump>,
     pub mutations: Mutations<'bump>,
-    pub stack: DiffStack<'bump>,
+    pub(crate) stack: DiffStack<'bump>,
     pub seen_scopes: FxHashSet<ScopeId>,
     pub seen_scopes: FxHashSet<ScopeId>,
-    pub cfg: DiffCfg,
-}
-
-pub(crate) struct DiffCfg {
     pub force_diff: bool,
     pub force_diff: bool,
 }
 }
-impl Default for DiffCfg {
-    fn default() -> Self {
-        Self {
-            force_diff: Default::default(),
-        }
-    }
-}
 
 
-/// a "saved" form of a diff machine
-/// in regular diff machine, the &'bump reference is a stack borrow, but the
-/// bump lifetimes are heap borrows.
-pub(crate) struct SavedDiffWork<'bump> {
-    pub mutations: Mutations<'bump>,
-    pub stack: DiffStack<'bump>,
-    pub seen_scopes: FxHashSet<ScopeId>,
-}
-
-impl<'a> SavedDiffWork<'a> {
-    pub unsafe fn extend(self: SavedDiffWork<'a>) -> SavedDiffWork<'static> {
-        std::mem::transmute(self)
-    }
-
-    pub unsafe fn promote<'b>(self, vdom: &'b ResourcePool) -> DiffMachine<'b> {
-        let extended: SavedDiffWork<'b> = std::mem::transmute(self);
-        DiffMachine {
-            vdom,
-            cfg: DiffCfg::default(),
-            mutations: extended.mutations,
-            stack: extended.stack,
-            seen_scopes: extended.seen_scopes,
-        }
-    }
-}
-
-impl<'bump> DiffMachine<'bump> {
-    pub(crate) fn new(mutations: Mutations<'bump>, shared: &'bump ResourcePool) -> Self {
+impl<'bump> DiffState<'bump> {
+    pub(crate) fn new(mutations: Mutations<'bump>) -> Self {
         Self {
         Self {
             mutations,
             mutations,
-            cfg: DiffCfg::default(),
             stack: DiffStack::new(),
             stack: DiffStack::new(),
-            vdom: shared,
-            seen_scopes: FxHashSet::default(),
+            seen_scopes: Default::default(),
+            force_diff: false,
         }
         }
     }
     }
+}
 
 
-    pub fn save(self) -> SavedDiffWork<'bump> {
-        SavedDiffWork {
-            mutations: self.mutations,
-            stack: self.stack,
-            seen_scopes: self.seen_scopes,
-        }
-    }
+impl<'bump> ScopeArena {
+    pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
+        // if let Some(component) = self.get_scope(id) {
 
 
-    pub fn diff_scope(&mut self, id: ScopeId) {
-        if let Some(component) = self.vdom.get_scope_mut(id) {
-            let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
-            self.stack.push(DiffInstruction::Diff { new, old });
-            self.work(|| false);
-        }
+        let (old, new) = (self.wip_head(id), self.fin_head(id));
+
+        state.stack.push(DiffInstruction::Diff { old, new });
+        self.work(state, || false);
+        // }
     }
     }
 
 
     /// Progress the diffing for this "fiber"
     /// Progress the diffing for this "fiber"
@@ -183,14 +141,21 @@ impl<'bump> DiffMachine<'bump> {
     /// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
     /// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
     ///
     ///
     /// Returns a `bool` indicating that the work completed properly.
     /// Returns a `bool` indicating that the work completed properly.
-    pub fn work(&mut self, mut deadline_expired: impl FnMut() -> bool) -> bool {
-        while let Some(instruction) = self.stack.pop() {
+    pub fn work(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        mut deadline_expired: impl FnMut() -> bool,
+    ) -> bool {
+        while let Some(instruction) = state.stack.pop() {
             match instruction {
             match instruction {
-                DiffInstruction::Diff { old, new } => self.diff_node(old, new),
-                DiffInstruction::Create { node } => self.create_node(node),
-                DiffInstruction::Mount { and } => self.mount(and),
-                DiffInstruction::PrepareMove { node } => self.prepare_move_node(node),
-                DiffInstruction::PopScope => self.stack.pop_off_scope(),
+                DiffInstruction::Diff { old, new } => self.diff_node(state, old, new),
+                DiffInstruction::Create { node } => self.create_node(state, node),
+                DiffInstruction::Mount { and } => self.mount(state, and),
+                DiffInstruction::PrepareMove { node } => {
+                    let num_on_stack = self.push_all_nodes(state, node);
+                    state.stack.add_child_count(num_on_stack);
+                }
+                DiffInstruction::PopScope => state.stack.pop_off_scope(),
             };
             };
 
 
             if deadline_expired() {
             if deadline_expired() {
@@ -202,48 +167,84 @@ impl<'bump> DiffMachine<'bump> {
         true
         true
     }
     }
 
 
-    fn prepare_move_node(&mut self, node: &'bump VNode<'bump>) {
-        for el in RealChildIterator::new(node, self.vdom) {
-            self.mutations.push_root(el.mounted_id());
-            self.stack.add_child_count(1);
+    // recursively push all the nodes of a tree onto the stack and return how many are there
+    fn push_all_nodes(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        node: &'bump VNode<'bump>,
+    ) -> usize {
+        match node {
+            VNode::Text(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
+                state.mutations.push_root(node.mounted_id());
+                1
+            }
+
+            VNode::Linked(linked) => {
+                todo!("load linked");
+                0
+                // let num_on_stack = linked.children.iter().map(|child| {
+                //     self.push_all_nodes(state, child)
+                // }).sum();
+                // state.mutations.push_root(node.mounted_id());
+                // num_on_stack + 1
+            }
+
+            VNode::Fragment(_) | VNode::Component(_) => {
+                //
+                let mut added = 0;
+                for child in node.children() {
+                    added += self.push_all_nodes(state, child);
+                }
+                added
+            }
+
+            VNode::Element(el) => {
+                let mut num_on_stack = 0;
+                for child in el.children.iter() {
+                    num_on_stack += self.push_all_nodes(state, child);
+                }
+                state.mutations.push_root(el.dom_id.get().unwrap());
+
+                num_on_stack + 1
+            }
         }
         }
     }
     }
 
 
-    fn mount(&mut self, and: MountType<'bump>) {
-        let nodes_created = self.stack.pop_nodes_created();
+    fn mount(&'bump self, state: &mut DiffState<'bump>, and: MountType<'bump>) {
+        let nodes_created = state.stack.pop_nodes_created();
         match and {
         match and {
             // add the nodes from this virtual list to the parent
             // add the nodes from this virtual list to the parent
             // used by fragments and components
             // used by fragments and components
             MountType::Absorb => {
             MountType::Absorb => {
-                self.stack.add_child_count(nodes_created);
+                state.stack.add_child_count(nodes_created);
             }
             }
 
 
             MountType::Replace { old } => {
             MountType::Replace { old } => {
                 if let Some(old_id) = old.try_mounted_id() {
                 if let Some(old_id) = old.try_mounted_id() {
-                    self.mutations.replace_with(old_id, nodes_created as u32);
-                    self.remove_nodes(Some(old), true);
+                    state.mutations.replace_with(old_id, nodes_created as u32);
+                    self.remove_nodes(state, Some(old), true);
                 } else {
                 } else {
                     if let Some(id) = self.find_first_element_id(old) {
                     if let Some(id) = self.find_first_element_id(old) {
-                        self.mutations.replace_with(id, nodes_created as u32);
+                        state.mutations.replace_with(id, nodes_created as u32);
                     }
                     }
-                    self.remove_nodes(Some(old), true);
+                    self.remove_nodes(state, Some(old), true);
                 }
                 }
             }
             }
 
 
             MountType::Append => {
             MountType::Append => {
-                self.mutations.edits.push(AppendChildren {
+                state.mutations.edits.push(AppendChildren {
                     many: nodes_created as u32,
                     many: nodes_created as u32,
                 });
                 });
             }
             }
 
 
             MountType::InsertAfter { other_node } => {
             MountType::InsertAfter { other_node } => {
                 let root = self.find_last_element(other_node).unwrap();
                 let root = self.find_last_element(other_node).unwrap();
-                self.mutations.insert_after(root, nodes_created as u32);
+                state.mutations.insert_after(root, nodes_created as u32);
             }
             }
 
 
             MountType::InsertBefore { other_node } => {
             MountType::InsertBefore { other_node } => {
                 let root = self.find_first_element_id(other_node).unwrap();
                 let root = self.find_first_element_id(other_node).unwrap();
-                self.mutations.insert_before(root, nodes_created as u32);
+                state.mutations.insert_before(root, nodes_created as u32);
             }
             }
         }
         }
     }
     }
@@ -252,42 +253,63 @@ impl<'bump> DiffMachine<'bump> {
     //  Tools for creating new nodes
     //  Tools for creating new nodes
     // =================================
     // =================================
 
 
-    fn create_node(&mut self, node: &'bump VNode<'bump>) {
+    fn create_node(&'bump self, state: &mut DiffState<'bump>, node: &'bump VNode<'bump>) {
         match node {
         match node {
-            VNode::Text(vtext) => self.create_text_node(vtext, node),
-            VNode::Suspended(suspended) => self.create_suspended_node(suspended, node),
-            VNode::Anchor(anchor) => self.create_anchor_node(anchor, node),
-            VNode::Element(element) => self.create_element_node(element, node),
-            VNode::Fragment(frag) => self.create_fragment_node(frag),
-            VNode::Component(component) => self.create_component_node(component),
+            VNode::Text(vtext) => self.create_text_node(state, vtext, node),
+            VNode::Suspended(suspended) => self.create_suspended_node(state, suspended, node),
+            VNode::Anchor(anchor) => self.create_anchor_node(state, anchor, node),
+            VNode::Element(element) => self.create_element_node(state, element, node),
+            VNode::Fragment(frag) => self.create_fragment_node(state, frag),
+            VNode::Component(component) => self.create_component_node(state, component),
+            VNode::Linked(linked) => self.create_linked_node(state, linked),
         }
         }
     }
     }
 
 
-    fn create_text_node(&mut self, vtext: &'bump VText<'bump>, node: &'bump VNode<'bump>) {
-        let real_id = self.vdom.reserve_node(node);
-        self.mutations.create_text_node(vtext.text, real_id);
+    fn create_text_node(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        vtext: &'bump VText<'bump>,
+        node: &'bump VNode<'bump>,
+    ) {
+        let real_id = self.reserve_node(node);
+        state.mutations.create_text_node(vtext.text, real_id);
         vtext.dom_id.set(Some(real_id));
         vtext.dom_id.set(Some(real_id));
-        self.stack.add_child_count(1);
+        state.stack.add_child_count(1);
     }
     }
 
 
-    fn create_suspended_node(&mut self, suspended: &'bump VSuspended, node: &'bump VNode<'bump>) {
-        let real_id = self.vdom.reserve_node(node);
-        self.mutations.create_placeholder(real_id);
+    fn create_suspended_node(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        suspended: &'bump VSuspended,
+        node: &'bump VNode<'bump>,
+    ) {
+        let real_id = self.reserve_node(node);
+        state.mutations.create_placeholder(real_id);
 
 
         suspended.dom_id.set(Some(real_id));
         suspended.dom_id.set(Some(real_id));
-        self.stack.add_child_count(1);
+        state.stack.add_child_count(1);
 
 
-        self.attach_suspended_node_to_scope(suspended);
+        self.attach_suspended_node_to_scope(state, suspended);
     }
     }
 
 
-    fn create_anchor_node(&mut self, anchor: &'bump VAnchor, node: &'bump VNode<'bump>) {
-        let real_id = self.vdom.reserve_node(node);
-        self.mutations.create_placeholder(real_id);
+    fn create_anchor_node(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        anchor: &'bump VAnchor,
+        node: &'bump VNode<'bump>,
+    ) {
+        let real_id = self.reserve_node(node);
+        state.mutations.create_placeholder(real_id);
         anchor.dom_id.set(Some(real_id));
         anchor.dom_id.set(Some(real_id));
-        self.stack.add_child_count(1);
+        state.stack.add_child_count(1);
     }
     }
 
 
-    fn create_element_node(&mut self, element: &'bump VElement<'bump>, node: &'bump VNode<'bump>) {
+    fn create_element_node(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        element: &'bump VElement<'bump>,
+        node: &'bump VNode<'bump>,
+    ) {
         let VElement {
         let VElement {
             tag_name,
             tag_name,
             listeners,
             listeners,
@@ -298,75 +320,90 @@ impl<'bump> DiffMachine<'bump> {
             ..
             ..
         } = element;
         } = element;
 
 
-        let real_id = self.vdom.reserve_node(node);
+        let real_id = self.reserve_node(node);
 
 
         dom_id.set(Some(real_id));
         dom_id.set(Some(real_id));
 
 
-        self.mutations.create_element(tag_name, *namespace, real_id);
+        state
+            .mutations
+            .create_element(tag_name, *namespace, real_id);
 
 
-        self.stack.add_child_count(1);
+        state.stack.add_child_count(1);
 
 
-        if let Some(cur_scope_id) = self.stack.current_scope() {
-            let scope = self.vdom.get_scope(cur_scope_id).unwrap();
+        if let Some(cur_scope_id) = state.stack.current_scope() {
+            let scope = self.get_scope(&cur_scope_id).unwrap();
 
 
-            listeners.iter().for_each(|listener| {
+            for listener in *listeners {
                 self.attach_listener_to_scope(listener, scope);
                 self.attach_listener_to_scope(listener, scope);
                 listener.mounted_node.set(Some(real_id));
                 listener.mounted_node.set(Some(real_id));
-                self.mutations.new_event_listener(listener, cur_scope_id);
-            });
+                state.mutations.new_event_listener(listener, cur_scope_id);
+            }
         } else {
         } else {
             log::warn!("create element called with no scope on the stack - this is an error for a live dom");
             log::warn!("create element called with no scope on the stack - this is an error for a live dom");
         }
         }
 
 
         for attr in *attributes {
         for attr in *attributes {
-            self.mutations.set_attribute(attr, real_id.as_u64());
+            state.mutations.set_attribute(attr, real_id.as_u64());
         }
         }
 
 
         if !children.is_empty() {
         if !children.is_empty() {
-            self.stack.create_children(children, MountType::Append);
+            state.stack.create_children(children, MountType::Append);
         }
         }
     }
     }
 
 
-    fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
-        self.stack.create_children(frag.children, MountType::Absorb);
+    fn create_fragment_node(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        frag: &'bump VFragment<'bump>,
+    ) {
+        state
+            .stack
+            .create_children(frag.children, MountType::Absorb);
     }
     }
 
 
-    fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
-        let caller = vcomponent.caller;
+    fn create_component_node(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        vcomponent: &'bump VComponent<'bump>,
+    ) {
+        // let caller = vcomponent.caller;
 
 
-        let parent_idx = self.stack.current_scope().unwrap();
+        let parent_idx = state.stack.current_scope().unwrap();
 
 
-        let shared = self.vdom.channel.clone();
+        let shared = self.sender.clone();
 
 
         // Insert a new scope into our component list
         // Insert a new scope into our component list
-        let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
-
-        let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
-            ScopeInner::new(
-                caller,
-                new_idx,
-                Some(parent_idx),
-                parent_scope.height + 1,
-                parent_scope.subtree(),
-                shared,
-            )
-        });
+        let parent_scope = self.get_scope(&parent_idx).unwrap();
+
+        let new_idx: ScopeId = todo!();
+        // self
+        //     .new_with_key(fc_ptr, vcomp, parent_scope, height, subtree, sender);
+
+        // .(|new_idx| {
+        //     // ScopeInner::new(
+        //         vcomponent,
+        //         new_idx,
+        //         Some(parent_idx),
+        //         parent_scope.height + 1,
+        //         parent_scope.subtree(),
+        //         shared,
+        //     // )
+        // });
 
 
         // Actually initialize the caller's slot with the right address
         // Actually initialize the caller's slot with the right address
         vcomponent.associated_scope.set(Some(new_idx));
         vcomponent.associated_scope.set(Some(new_idx));
 
 
         if !vcomponent.can_memoize {
         if !vcomponent.can_memoize {
-            let cur_scope = self.vdom.get_scope_mut(parent_idx).unwrap();
-            let extended = vcomponent as *const VComponent;
-            let extended: *const VComponent<'static> = unsafe { std::mem::transmute(extended) };
-            cur_scope.borrowed_props.borrow_mut().push(extended);
+            let cur_scope = self.get_scope(&parent_idx).unwrap();
+            let extended = unsafe { std::mem::transmute(vcomponent) };
+            cur_scope.items.get_mut().borrowed_props.push(extended);
         }
         }
 
 
         // TODO:
         // TODO:
         //  add noderefs to current noderef list Noderefs
         //  add noderefs to current noderef list Noderefs
         //  add effects to current effect list Effects
         //  add effects to current effect list Effects
 
 
-        let new_component = self.vdom.get_scope_mut(new_idx).unwrap();
+        let new_component = self.get_scope(&new_idx).unwrap();
 
 
         log::debug!(
         log::debug!(
             "initializing component {:?} with height {:?}",
             "initializing component {:?} with height {:?}",
@@ -375,58 +412,79 @@ impl<'bump> DiffMachine<'bump> {
         );
         );
 
 
         // Run the scope for one iteration to initialize it
         // Run the scope for one iteration to initialize it
-        if new_component.run_scope(self.vdom) {
-            // Take the node that was just generated from running the component
-            let nextnode = new_component.frames.fin_head();
-            self.stack.create_component(new_idx, nextnode);
+        //
+        todo!("run scope");
+        // if new_component.run_scope(self) {
+        //     // Take the node that was just generated from running the component
+        //     let nextnode = new_component.frames.fin_head();
+        //     state.stack.create_component(new_idx, nextnode);
 
 
-            //
-            /*
-            tree_item {
+        //     //
+        //     /*
+        //     tree_item {
 
 
-            }
+        //     }
 
 
-            */
-            if new_component.is_subtree_root.get() {
-                self.stack.push_subtree();
-            }
-        }
+        //     */
+        //     if new_component.is_subtree_root.get() {
+        //         state.stack.push_subtree();
+        //     }
+        // }
 
 
         // Finally, insert this scope as a seen node.
         // Finally, insert this scope as a seen node.
-        self.seen_scopes.insert(new_idx);
+        state.seen_scopes.insert(new_idx);
+    }
+
+    fn create_linked_node(&'bump self, state: &mut DiffState<'bump>, link: &'bump NodeLink) {
+        todo!()
     }
     }
 
 
     // =================================
     // =================================
     //  Tools for diffing nodes
     //  Tools for diffing nodes
     // =================================
     // =================================
 
 
-    pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
+    pub fn diff_node(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old_node: &'bump VNode<'bump>,
+        new_node: &'bump VNode<'bump>,
+    ) {
         use VNode::*;
         use VNode::*;
         match (old_node, new_node) {
         match (old_node, new_node) {
             // Check the most common cases first
             // Check the most common cases first
-            (Text(old), Text(new)) => self.diff_text_nodes(old, new),
+            (Text(old), Text(new)) => self.diff_text_nodes(state, old, new),
             (Component(old), Component(new)) => {
             (Component(old), Component(new)) => {
-                self.diff_component_nodes(old_node, new_node, old, new)
+                self.diff_component_nodes(state, old_node, new_node, old, new)
             }
             }
-            (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
+            (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(state, old, new),
             (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
             (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
-            (Suspended(old), Suspended(new)) => self.diff_suspended_nodes(old, new),
-            (Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
+            (Suspended(old), Suspended(new)) => self.diff_suspended_nodes(state, old, new),
+            (Element(old), Element(new)) => {
+                self.diff_element_nodes(state, old, new, old_node, new_node)
+            }
+            (Linked(old), Linked(new)) => self.diff_linked_nodes(state, old, new),
 
 
             // Anything else is just a basic replace and create
             // Anything else is just a basic replace and create
             (
             (
-                Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) | Suspended(_),
-                Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) | Suspended(_),
-            ) => self
+                Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_)
+                | Suspended(_),
+                Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_)
+                | Suspended(_),
+            ) => state
                 .stack
                 .stack
                 .create_node(new_node, MountType::Replace { old: old_node }),
                 .create_node(new_node, MountType::Replace { old: old_node }),
         }
         }
     }
     }
 
 
-    fn diff_text_nodes(&mut self, old: &'bump VText<'bump>, new: &'bump VText<'bump>) {
+    fn diff_text_nodes(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump VText<'bump>,
+        new: &'bump VText<'bump>,
+    ) {
         if let Some(root) = old.dom_id.get() {
         if let Some(root) = old.dom_id.get() {
             if old.text != new.text {
             if old.text != new.text {
-                self.mutations.set_text(new.text, root.as_u64());
+                state.mutations.set_text(new.text, root.as_u64());
             }
             }
 
 
             new.dom_id.set(Some(root));
             new.dom_id.set(Some(root));
@@ -434,7 +492,8 @@ impl<'bump> DiffMachine<'bump> {
     }
     }
 
 
     fn diff_element_nodes(
     fn diff_element_nodes(
-        &mut self,
+        &'bump self,
+        state: &mut DiffState<'bump>,
         old: &'bump VElement<'bump>,
         old: &'bump VElement<'bump>,
         new: &'bump VElement<'bump>,
         new: &'bump VElement<'bump>,
         old_node: &'bump VNode<'bump>,
         old_node: &'bump VNode<'bump>,
@@ -449,11 +508,11 @@ impl<'bump> DiffMachine<'bump> {
         if new.tag_name != old.tag_name || new.namespace != old.namespace {
         if new.tag_name != old.tag_name || new.namespace != old.namespace {
             // maybe make this an instruction?
             // maybe make this an instruction?
             // issue is that we need the "vnode" but this method only has the velement
             // issue is that we need the "vnode" but this method only has the velement
-            self.stack.push_nodes_created(0);
-            self.stack.push(DiffInstruction::Mount {
+            state.stack.push_nodes_created(0);
+            state.stack.push(DiffInstruction::Mount {
                 and: MountType::Replace { old: old_node },
                 and: MountType::Replace { old: old_node },
             });
             });
-            self.create_element_node(new, new_node);
+            self.create_element_node(state, new, new_node);
             return;
             return;
         }
         }
 
 
@@ -473,15 +532,15 @@ impl<'bump> DiffMachine<'bump> {
         if old.attributes.len() == new.attributes.len() {
         if old.attributes.len() == new.attributes.len() {
             for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
             for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
                 if old_attr.value != new_attr.value || new_attr.is_volatile {
                 if old_attr.value != new_attr.value || new_attr.is_volatile {
-                    self.mutations.set_attribute(new_attr, root.as_u64());
+                    state.mutations.set_attribute(new_attr, root.as_u64());
                 }
                 }
             }
             }
         } else {
         } else {
             for attribute in old.attributes {
             for attribute in old.attributes {
-                self.mutations.remove_attribute(attribute, root.as_u64());
+                state.mutations.remove_attribute(attribute, root.as_u64());
             }
             }
             for attribute in new.attributes {
             for attribute in new.attributes {
-                self.mutations.set_attribute(attribute, root.as_u64())
+                state.mutations.set_attribute(attribute, root.as_u64())
             }
             }
         }
         }
 
 
@@ -493,44 +552,47 @@ impl<'bump> DiffMachine<'bump> {
         // We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
         // We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
         //
         //
         // TODO: take a more efficient path than this
         // TODO: take a more efficient path than this
-        if let Some(cur_scope_id) = self.stack.current_scope() {
-            let scope = self.vdom.get_scope(cur_scope_id).unwrap();
+        if let Some(cur_scope_id) = state.stack.current_scope() {
+            let scope = self.get_scope(&cur_scope_id).unwrap();
 
 
             if old.listeners.len() == new.listeners.len() {
             if old.listeners.len() == new.listeners.len() {
                 for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
                 for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
                     if old_l.event != new_l.event {
                     if old_l.event != new_l.event {
-                        self.mutations
+                        state
+                            .mutations
                             .remove_event_listener(old_l.event, root.as_u64());
                             .remove_event_listener(old_l.event, root.as_u64());
-                        self.mutations.new_event_listener(new_l, cur_scope_id);
+                        state.mutations.new_event_listener(new_l, cur_scope_id);
                     }
                     }
                     new_l.mounted_node.set(old_l.mounted_node.get());
                     new_l.mounted_node.set(old_l.mounted_node.get());
                     self.attach_listener_to_scope(new_l, scope);
                     self.attach_listener_to_scope(new_l, scope);
                 }
                 }
             } else {
             } else {
                 for listener in old.listeners {
                 for listener in old.listeners {
-                    self.mutations
+                    state
+                        .mutations
                         .remove_event_listener(listener.event, root.as_u64());
                         .remove_event_listener(listener.event, root.as_u64());
                 }
                 }
                 for listener in new.listeners {
                 for listener in new.listeners {
                     listener.mounted_node.set(Some(root));
                     listener.mounted_node.set(Some(root));
-                    self.mutations.new_event_listener(listener, cur_scope_id);
+                    state.mutations.new_event_listener(listener, cur_scope_id);
                     self.attach_listener_to_scope(listener, scope);
                     self.attach_listener_to_scope(listener, scope);
                 }
                 }
             }
             }
         }
         }
 
 
         if old.children.is_empty() && !new.children.is_empty() {
         if old.children.is_empty() && !new.children.is_empty() {
-            self.mutations.edits.push(PushRoot {
+            state.mutations.edits.push(PushRoot {
                 root: root.as_u64(),
                 root: root.as_u64(),
             });
             });
-            self.stack.create_children(new.children, MountType::Append);
+            state.stack.create_children(new.children, MountType::Append);
         } else {
         } else {
-            self.diff_children(old.children, new.children);
+            self.diff_children(state, old.children, new.children);
         }
         }
     }
     }
 
 
     fn diff_component_nodes(
     fn diff_component_nodes(
-        &mut self,
+        &'bump self,
+        state: &mut DiffState<'bump>,
         old_node: &'bump VNode<'bump>,
         old_node: &'bump VNode<'bump>,
         new_node: &'bump VNode<'bump>,
         new_node: &'bump VNode<'bump>,
 
 
@@ -543,50 +605,74 @@ impl<'bump> DiffMachine<'bump> {
         if old.user_fc == new.user_fc {
         if old.user_fc == new.user_fc {
             log::debug!("Diffing component {:?} - {:?}", new.user_fc, scope_addr);
             log::debug!("Diffing component {:?} - {:?}", new.user_fc, scope_addr);
             //
             //
-            self.stack.scope_stack.push(scope_addr);
+            state.stack.scope_stack.push(scope_addr);
 
 
             // Make sure the new component vnode is referencing the right scope id
             // Make sure the new component vnode is referencing the right scope id
             new.associated_scope.set(Some(scope_addr));
             new.associated_scope.set(Some(scope_addr));
 
 
             // make sure the component's caller function is up to date
             // make sure the component's caller function is up to date
-            let scope = self.vdom.get_scope_mut(scope_addr).unwrap();
-            scope.update_scope_dependencies(new.caller);
+            let scope = self.get_scope(&scope_addr).unwrap();
+            let mut items = scope.items.borrow_mut();
 
 
             // React doesn't automatically memoize, but we do.
             // React doesn't automatically memoize, but we do.
-            let props_are_the_same = old.comparator.unwrap();
+            let props_are_the_same = todo!("reworking component memoization");
+            // let props_are_the_same = todo!("reworking component memoization");
+            // let props_are_the_same = old.comparator.unwrap();
 
 
-            if self.cfg.force_diff || !props_are_the_same(new) {
-                let succeeded = scope.run_scope(self.vdom);
+            // if self.cfg.force_diff || !props_are_the_same(new) {
+            //     let succeeded = scope.run_scope(self);
 
 
-                if succeeded {
-                    self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
-                }
-            }
+            //     if succeeded {
+            //         self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
+            //     }
+            // }
 
 
-            self.stack.scope_stack.pop();
+            state.stack.scope_stack.pop();
         } else {
         } else {
-            self.stack
+            state
+                .stack
                 .create_node(new_node, MountType::Replace { old: old_node });
                 .create_node(new_node, MountType::Replace { old: old_node });
         }
         }
     }
     }
 
 
-    fn diff_fragment_nodes(&mut self, old: &'bump VFragment<'bump>, new: &'bump VFragment<'bump>) {
+    fn diff_fragment_nodes(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump VFragment<'bump>,
+        new: &'bump VFragment<'bump>,
+    ) {
         // This is the case where options or direct vnodes might be used.
         // This is the case where options or direct vnodes might be used.
         // In this case, it's faster to just skip ahead to their diff
         // In this case, it's faster to just skip ahead to their diff
         if old.children.len() == 1 && new.children.len() == 1 {
         if old.children.len() == 1 && new.children.len() == 1 {
-            self.diff_node(&old.children[0], &new.children[0]);
+            self.diff_node(state, &old.children[0], &new.children[0]);
             return;
             return;
         }
         }
 
 
         debug_assert!(!old.children.is_empty());
         debug_assert!(!old.children.is_empty());
         debug_assert!(!new.children.is_empty());
         debug_assert!(!new.children.is_empty());
 
 
-        self.diff_children(old.children, new.children);
+        self.diff_children(state, old.children, new.children);
     }
     }
 
 
-    fn diff_suspended_nodes(&mut self, old: &'bump VSuspended, new: &'bump VSuspended) {
+    fn diff_suspended_nodes(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump VSuspended,
+        new: &'bump VSuspended,
+    ) {
         new.dom_id.set(old.dom_id.get());
         new.dom_id.set(old.dom_id.get());
-        self.attach_suspended_node_to_scope(new);
+        self.attach_suspended_node_to_scope(state, new);
+    }
+
+    fn diff_linked_nodes(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump NodeLink,
+        new: &'bump NodeLink,
+    ) {
+        todo!();
+        // new.dom_id.set(old.dom_id.get());
+        // self.attach_linked_node_to_scope(state, new);
     }
     }
 
 
     // =============================================
     // =============================================
@@ -608,26 +694,32 @@ impl<'bump> DiffMachine<'bump> {
     //
     //
     // Fragment nodes cannot generate empty children lists, so we can assume that when a list is empty, it belongs only
     // Fragment nodes cannot generate empty children lists, so we can assume that when a list is empty, it belongs only
     // to an element, and appending makes sense.
     // to an element, and appending makes sense.
-    fn diff_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
+    fn diff_children(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump [VNode<'bump>],
+        new: &'bump [VNode<'bump>],
+    ) {
         // Remember, fragments can never be empty (they always have a single child)
         // Remember, fragments can never be empty (they always have a single child)
         match (old, new) {
         match (old, new) {
             ([], []) => {}
             ([], []) => {}
             ([], _) => {
             ([], _) => {
                 // we need to push the
                 // we need to push the
-                self.stack.create_children(new, MountType::Append);
+                state.stack.create_children(new, MountType::Append);
             }
             }
             (_, []) => {
             (_, []) => {
-                self.remove_nodes(old, true);
+                self.remove_nodes(state, old, true);
             }
             }
             ([VNode::Anchor(old_anchor)], [VNode::Anchor(new_anchor)]) => {
             ([VNode::Anchor(old_anchor)], [VNode::Anchor(new_anchor)]) => {
                 old_anchor.dom_id.set(new_anchor.dom_id.get());
                 old_anchor.dom_id.set(new_anchor.dom_id.get());
             }
             }
             ([VNode::Anchor(_)], _) => {
             ([VNode::Anchor(_)], _) => {
-                self.stack
+                state
+                    .stack
                     .create_children(new, MountType::Replace { old: &old[0] });
                     .create_children(new, MountType::Replace { old: &old[0] });
             }
             }
             (_, [VNode::Anchor(_)]) => {
             (_, [VNode::Anchor(_)]) => {
-                self.replace_and_create_many_with_one(old, &new[0]);
+                self.replace_and_create_many_with_one(state, old, &new[0]);
             }
             }
             _ => {
             _ => {
                 let new_is_keyed = new[0].key().is_some();
                 let new_is_keyed = new[0].key().is_some();
@@ -643,9 +735,9 @@ impl<'bump> DiffMachine<'bump> {
                 );
                 );
 
 
                 if new_is_keyed && old_is_keyed {
                 if new_is_keyed && old_is_keyed {
-                    self.diff_keyed_children(old, new);
+                    self.diff_keyed_children(state, old, new);
                 } else {
                 } else {
-                    self.diff_non_keyed_children(old, new);
+                    self.diff_non_keyed_children(state, old, new);
                 }
                 }
             }
             }
         }
         }
@@ -659,20 +751,25 @@ impl<'bump> DiffMachine<'bump> {
     //     [... parent]
     //     [... parent]
     //
     //
     // the change list stack is in the same state when this function returns.
     // the change list stack is in the same state when this function returns.
-    fn diff_non_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
+    fn diff_non_keyed_children(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump [VNode<'bump>],
+        new: &'bump [VNode<'bump>],
+    ) {
         // Handled these cases in `diff_children` before calling this function.
         // Handled these cases in `diff_children` before calling this function.
         debug_assert!(!new.is_empty());
         debug_assert!(!new.is_empty());
         debug_assert!(!old.is_empty());
         debug_assert!(!old.is_empty());
 
 
         for (new, old) in new.iter().zip(old.iter()).rev() {
         for (new, old) in new.iter().zip(old.iter()).rev() {
-            self.stack.push(DiffInstruction::Diff { new, old });
+            state.stack.push(DiffInstruction::Diff { new, old });
         }
         }
 
 
         use std::cmp::Ordering;
         use std::cmp::Ordering;
         match old.len().cmp(&new.len()) {
         match old.len().cmp(&new.len()) {
-            Ordering::Greater => self.remove_nodes(&old[new.len()..], true),
+            Ordering::Greater => self.remove_nodes(state, &old[new.len()..], true),
             Ordering::Less => {
             Ordering::Less => {
-                self.stack.create_children(
+                state.stack.create_children(
                     &new[old.len()..],
                     &new[old.len()..],
                     MountType::InsertAfter {
                     MountType::InsertAfter {
                         other_node: old.last().unwrap(),
                         other_node: old.last().unwrap(),
@@ -701,7 +798,12 @@ impl<'bump> DiffMachine<'bump> {
     // https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
     // https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
     //
     //
     // The stack is empty upon entry.
     // The stack is empty upon entry.
-    fn diff_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
+    fn diff_keyed_children(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump [VNode<'bump>],
+        new: &'bump [VNode<'bump>],
+    ) {
         if cfg!(debug_assertions) {
         if cfg!(debug_assertions) {
             let mut keys = fxhash::FxHashSet::default();
             let mut keys = fxhash::FxHashSet::default();
             let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
             let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
@@ -729,7 +831,7 @@ impl<'bump> DiffMachine<'bump> {
         //
         //
         // `shared_prefix_count` is the count of how many nodes at the start of
         // `shared_prefix_count` is the count of how many nodes at the start of
         // `new` and `old` share the same keys.
         // `new` and `old` share the same keys.
-        let (left_offset, right_offset) = match self.diff_keyed_ends(old, new) {
+        let (left_offset, right_offset) = match self.diff_keyed_ends(state, old, new) {
             Some(count) => count,
             Some(count) => count,
             None => return,
             None => return,
         };
         };
@@ -739,7 +841,7 @@ impl<'bump> DiffMachine<'bump> {
         //     right_offset,
         //     right_offset,
         // );
         // );
 
 
-        // log::debug!("stack before lo is {:#?}", self.stack.instructions);
+        // log::debug!("stack before lo is {:#?}", state.stack.instructions);
         // Ok, we now hopefully have a smaller range of children in the middle
         // Ok, we now hopefully have a smaller range of children in the middle
         // within which to re-order nodes with the same keys, remove old nodes with
         // within which to re-order nodes with the same keys, remove old nodes with
         // now-unused keys, and create new nodes with fresh keys.
         // now-unused keys, and create new nodes with fresh keys.
@@ -753,14 +855,14 @@ impl<'bump> DiffMachine<'bump> {
         );
         );
         if new_middle.is_empty() {
         if new_middle.is_empty() {
             // remove the old elements
             // remove the old elements
-            self.remove_nodes(old_middle, true);
+            self.remove_nodes(state, old_middle, true);
         } else if old_middle.is_empty() {
         } else if old_middle.is_empty() {
             // there were no old elements, so just create the new elements
             // there were no old elements, so just create the new elements
             // we need to find the right "foothold" though - we shouldn't use the "append" at all
             // we need to find the right "foothold" though - we shouldn't use the "append" at all
             if left_offset == 0 {
             if left_offset == 0 {
                 // insert at the beginning of the old list
                 // insert at the beginning of the old list
                 let foothold = &old[old.len() - right_offset];
                 let foothold = &old[old.len() - right_offset];
-                self.stack.create_children(
+                state.stack.create_children(
                     new_middle,
                     new_middle,
                     MountType::InsertBefore {
                     MountType::InsertBefore {
                         other_node: foothold,
                         other_node: foothold,
@@ -769,7 +871,7 @@ impl<'bump> DiffMachine<'bump> {
             } else if right_offset == 0 {
             } else if right_offset == 0 {
                 // insert at the end  the old list
                 // insert at the end  the old list
                 let foothold = old.last().unwrap();
                 let foothold = old.last().unwrap();
-                self.stack.create_children(
+                state.stack.create_children(
                     new_middle,
                     new_middle,
                     MountType::InsertAfter {
                     MountType::InsertAfter {
                         other_node: foothold,
                         other_node: foothold,
@@ -778,7 +880,7 @@ impl<'bump> DiffMachine<'bump> {
             } else {
             } else {
                 // inserting in the middle
                 // inserting in the middle
                 let foothold = &old[left_offset - 1];
                 let foothold = &old[left_offset - 1];
-                self.stack.create_children(
+                state.stack.create_children(
                     new_middle,
                     new_middle,
                     MountType::InsertAfter {
                     MountType::InsertAfter {
                         other_node: foothold,
                         other_node: foothold,
@@ -786,10 +888,10 @@ impl<'bump> DiffMachine<'bump> {
                 );
                 );
             }
             }
         } else {
         } else {
-            self.diff_keyed_middle(old_middle, new_middle);
+            self.diff_keyed_middle(state, old_middle, new_middle);
         }
         }
 
 
-        log::debug!("stack after km is {:#?}", self.stack.instructions);
+        log::debug!("stack after km is {:#?}", state.stack.instructions);
     }
     }
 
 
     /// Diff both ends of the children that share keys.
     /// Diff both ends of the children that share keys.
@@ -798,7 +900,8 @@ impl<'bump> DiffMachine<'bump> {
     ///
     ///
     /// If there is no offset, then this function returns None and the diffing is complete.
     /// If there is no offset, then this function returns None and the diffing is complete.
     fn diff_keyed_ends(
     fn diff_keyed_ends(
-        &mut self,
+        &'bump self,
+        state: &mut DiffState<'bump>,
         old: &'bump [VNode<'bump>],
         old: &'bump [VNode<'bump>],
         new: &'bump [VNode<'bump>],
         new: &'bump [VNode<'bump>],
     ) -> Option<(usize, usize)> {
     ) -> Option<(usize, usize)> {
@@ -809,14 +912,14 @@ impl<'bump> DiffMachine<'bump> {
             if old.key() != new.key() {
             if old.key() != new.key() {
                 break;
                 break;
             }
             }
-            self.stack.push(DiffInstruction::Diff { old, new });
+            state.stack.push(DiffInstruction::Diff { old, new });
             left_offset += 1;
             left_offset += 1;
         }
         }
 
 
         // If that was all of the old children, then create and append the remaining
         // If that was all of the old children, then create and append the remaining
         // new children and we're finished.
         // new children and we're finished.
         if left_offset == old.len() {
         if left_offset == old.len() {
-            self.stack.create_children(
+            state.stack.create_children(
                 &new[left_offset..],
                 &new[left_offset..],
                 MountType::InsertAfter {
                 MountType::InsertAfter {
                     other_node: old.last().unwrap(),
                     other_node: old.last().unwrap(),
@@ -828,7 +931,7 @@ impl<'bump> DiffMachine<'bump> {
         // And if that was all of the new children, then remove all of the remaining
         // And if that was all of the new children, then remove all of the remaining
         // old children and we're finished.
         // old children and we're finished.
         if left_offset == new.len() {
         if left_offset == new.len() {
-            self.remove_nodes(&old[left_offset..], true);
+            self.remove_nodes(state, &old[left_offset..], true);
             return None;
             return None;
         }
         }
 
 
@@ -839,7 +942,7 @@ impl<'bump> DiffMachine<'bump> {
             if old.key() != new.key() {
             if old.key() != new.key() {
                 break;
                 break;
             }
             }
-            self.diff_node(old, new);
+            self.diff_node(state, old, new);
             right_offset += 1;
             right_offset += 1;
         }
         }
 
 
@@ -859,7 +962,12 @@ impl<'bump> DiffMachine<'bump> {
     // This function will load the appropriate nodes onto the stack and do diffing in place.
     // This function will load the appropriate nodes onto the stack and do diffing in place.
     //
     //
     // Upon exit from this function, it will be restored to that same state.
     // Upon exit from this function, it will be restored to that same state.
-    fn diff_keyed_middle(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
+    fn diff_keyed_middle(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        old: &'bump [VNode<'bump>],
+        new: &'bump [VNode<'bump>],
+    ) {
         /*
         /*
         1. Map the old keys into a numerical ordering based on indices.
         1. Map the old keys into a numerical ordering based on indices.
         2. Create a map of old key to its index
         2. Create a map of old key to its index
@@ -923,7 +1031,7 @@ impl<'bump> DiffMachine<'bump> {
             log::debug!("old_key_to_old_index, {:#?}", old_key_to_old_index);
             log::debug!("old_key_to_old_index, {:#?}", old_key_to_old_index);
             log::debug!("new_index_to_old_index, {:#?}", new_index_to_old_index);
             log::debug!("new_index_to_old_index, {:#?}", new_index_to_old_index);
             log::debug!("shared_keys, {:#?}", shared_keys);
             log::debug!("shared_keys, {:#?}", shared_keys);
-            self.replace_and_create_many_with_many(old, new);
+            self.replace_and_create_many_with_many(state, old, new);
             return;
             return;
         }
         }
 
 
@@ -967,15 +1075,15 @@ impl<'bump> DiffMachine<'bump> {
         // add mount instruction for the last items not covered by the lis
         // add mount instruction for the last items not covered by the lis
         let first_lis = *lis_sequence.first().unwrap();
         let first_lis = *lis_sequence.first().unwrap();
         if first_lis > 0 {
         if first_lis > 0 {
-            self.stack.push_nodes_created(0);
-            self.stack.push(DiffInstruction::Mount {
+            state.stack.push_nodes_created(0);
+            state.stack.push(DiffInstruction::Mount {
                 and: MountType::InsertBefore {
                 and: MountType::InsertBefore {
                     other_node: &new[first_lis],
                     other_node: &new[first_lis],
                 },
                 },
             });
             });
 
 
             for (idx, new_node) in new[..first_lis].iter().enumerate().rev() {
             for (idx, new_node) in new[..first_lis].iter().enumerate().rev() {
-                apply(idx, new_node, &mut self.stack);
+                apply(idx, new_node, &mut state.stack);
             }
             }
         }
         }
 
 
@@ -984,14 +1092,14 @@ impl<'bump> DiffMachine<'bump> {
         let mut last = *lis_iter.next().unwrap();
         let mut last = *lis_iter.next().unwrap();
         for next in lis_iter {
         for next in lis_iter {
             if last - next > 1 {
             if last - next > 1 {
-                self.stack.push_nodes_created(0);
-                self.stack.push(DiffInstruction::Mount {
+                state.stack.push_nodes_created(0);
+                state.stack.push(DiffInstruction::Mount {
                     and: MountType::InsertBefore {
                     and: MountType::InsertBefore {
                         other_node: &new[last],
                         other_node: &new[last],
                     },
                     },
                 });
                 });
                 for (idx, new_node) in new[(next + 1)..last].iter().enumerate().rev() {
                 for (idx, new_node) in new[(next + 1)..last].iter().enumerate().rev() {
-                    apply(idx + next + 1, new_node, &mut self.stack);
+                    apply(idx + next + 1, new_node, &mut state.stack);
                 }
                 }
             }
             }
             last = *next;
             last = *next;
@@ -1000,19 +1108,19 @@ impl<'bump> DiffMachine<'bump> {
         // add mount instruction for the first items not covered by the lis
         // add mount instruction for the first items not covered by the lis
         let last = *lis_sequence.last().unwrap();
         let last = *lis_sequence.last().unwrap();
         if last < (new.len() - 1) {
         if last < (new.len() - 1) {
-            self.stack.push_nodes_created(0);
-            self.stack.push(DiffInstruction::Mount {
+            state.stack.push_nodes_created(0);
+            state.stack.push(DiffInstruction::Mount {
                 and: MountType::InsertAfter {
                 and: MountType::InsertAfter {
                     other_node: &new[last],
                     other_node: &new[last],
                 },
                 },
             });
             });
             for (idx, new_node) in new[(last + 1)..].iter().enumerate().rev() {
             for (idx, new_node) in new[(last + 1)..].iter().enumerate().rev() {
-                apply(idx + last + 1, new_node, &mut self.stack);
+                apply(idx + last + 1, new_node, &mut state.stack);
             }
             }
         }
         }
 
 
         for idx in lis_sequence.iter().rev() {
         for idx in lis_sequence.iter().rev() {
-            self.stack.push(DiffInstruction::Diff {
+            state.stack.push(DiffInstruction::Diff {
                 new: &new[*idx],
                 new: &new[*idx],
                 old: &old[new_index_to_old_index[*idx]],
                 old: &old[new_index_to_old_index[*idx]],
             });
             });
@@ -1023,7 +1131,7 @@ impl<'bump> DiffMachine<'bump> {
     //  Utilities
     //  Utilities
     // =====================
     // =====================
 
 
-    fn find_last_element(&mut self, vnode: &'bump VNode<'bump>) -> Option<ElementId> {
+    fn find_last_element(&'bump self, vnode: &'bump VNode<'bump>) -> Option<ElementId> {
         let mut search_node = Some(vnode);
         let mut search_node = Some(vnode);
 
 
         loop {
         loop {
@@ -1032,20 +1140,22 @@ impl<'bump> DiffMachine<'bump> {
                 VNode::Element(t) => break t.dom_id.get(),
                 VNode::Element(t) => break t.dom_id.get(),
                 VNode::Suspended(t) => break t.dom_id.get(),
                 VNode::Suspended(t) => break t.dom_id.get(),
                 VNode::Anchor(t) => break t.dom_id.get(),
                 VNode::Anchor(t) => break t.dom_id.get(),
-
+                VNode::Linked(_) => {
+                    todo!()
+                }
                 VNode::Fragment(frag) => {
                 VNode::Fragment(frag) => {
                     search_node = frag.children.last();
                     search_node = frag.children.last();
                 }
                 }
                 VNode::Component(el) => {
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.vdom.get_scope(scope_id).unwrap();
-                    search_node = Some(scope.root_node());
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    search_node = Some(self.root_node(&scope_id));
                 }
                 }
             }
             }
         }
         }
     }
     }
 
 
-    fn find_first_element_id(&mut self, vnode: &'bump VNode<'bump>) -> Option<ElementId> {
+    fn find_first_element_id(&'bump self, vnode: &'bump VNode<'bump>) -> Option<ElementId> {
         let mut search_node = Some(vnode);
         let mut search_node = Some(vnode);
 
 
         loop {
         loop {
@@ -1056,8 +1166,11 @@ impl<'bump> DiffMachine<'bump> {
                 }
                 }
                 VNode::Component(el) => {
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.vdom.get_scope(scope_id).unwrap();
-                    search_node = Some(scope.root_node());
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    search_node = Some(self.root_node(&scope_id));
+                }
+                VNode::Linked(link) => {
+                    todo!("linked")
                 }
                 }
                 VNode::Text(t) => break t.dom_id.get(),
                 VNode::Text(t) => break t.dom_id.get(),
                 VNode::Element(t) => break t.dom_id.get(),
                 VNode::Element(t) => break t.dom_id.get(),
@@ -1068,23 +1181,26 @@ impl<'bump> DiffMachine<'bump> {
     }
     }
 
 
     fn replace_and_create_many_with_one(
     fn replace_and_create_many_with_one(
-        &mut self,
+        &'bump self,
+        state: &mut DiffState<'bump>,
         old: &'bump [VNode<'bump>],
         old: &'bump [VNode<'bump>],
         new: &'bump VNode<'bump>,
         new: &'bump VNode<'bump>,
     ) {
     ) {
         if let Some(first_old) = old.get(0) {
         if let Some(first_old) = old.get(0) {
-            self.remove_nodes(&old[1..], true);
-            self.stack
+            self.remove_nodes(state, &old[1..], true);
+            state
+                .stack
                 .create_node(new, MountType::Replace { old: first_old });
                 .create_node(new, MountType::Replace { old: first_old });
         } else {
         } else {
-            self.stack.create_node(new, MountType::Append {});
+            state.stack.create_node(new, MountType::Append {});
         }
         }
     }
     }
 
 
     /// schedules nodes for garbage collection and pushes "remove" to the mutation stack
     /// schedules nodes for garbage collection and pushes "remove" to the mutation stack
     /// remove can happen whenever
     /// remove can happen whenever
     fn remove_nodes(
     fn remove_nodes(
-        &mut self,
+        &'bump self,
+        state: &mut DiffState<'bump>,
         nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
         nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
         gen_muts: bool,
         gen_muts: bool,
     ) {
     ) {
@@ -1093,50 +1209,54 @@ impl<'bump> DiffMachine<'bump> {
             match node {
             match node {
                 VNode::Text(t) => {
                 VNode::Text(t) => {
                     let id = t.dom_id.get().unwrap();
                     let id = t.dom_id.get().unwrap();
-                    self.vdom.collect_garbage(id);
+                    self.collect_garbage(id);
 
 
                     if gen_muts {
                     if gen_muts {
-                        self.mutations.remove(id.as_u64());
+                        state.mutations.remove(id.as_u64());
                     }
                     }
                 }
                 }
                 VNode::Suspended(s) => {
                 VNode::Suspended(s) => {
                     let id = s.dom_id.get().unwrap();
                     let id = s.dom_id.get().unwrap();
-                    self.vdom.collect_garbage(id);
+                    self.collect_garbage(id);
 
 
                     if gen_muts {
                     if gen_muts {
-                        self.mutations.remove(id.as_u64());
+                        state.mutations.remove(id.as_u64());
                     }
                     }
                 }
                 }
                 VNode::Anchor(a) => {
                 VNode::Anchor(a) => {
                     let id = a.dom_id.get().unwrap();
                     let id = a.dom_id.get().unwrap();
-                    self.vdom.collect_garbage(id);
+                    self.collect_garbage(id);
 
 
                     if gen_muts {
                     if gen_muts {
-                        self.mutations.remove(id.as_u64());
+                        state.mutations.remove(id.as_u64());
                     }
                     }
                 }
                 }
                 VNode::Element(e) => {
                 VNode::Element(e) => {
                     let id = e.dom_id.get().unwrap();
                     let id = e.dom_id.get().unwrap();
 
 
                     if gen_muts {
                     if gen_muts {
-                        self.mutations.remove(id.as_u64());
+                        state.mutations.remove(id.as_u64());
                     }
                     }
 
 
-                    self.remove_nodes(e.children, false);
+                    self.remove_nodes(state, e.children, false);
                 }
                 }
 
 
                 VNode::Fragment(f) => {
                 VNode::Fragment(f) => {
-                    self.remove_nodes(f.children, gen_muts);
+                    self.remove_nodes(state, f.children, gen_muts);
+                }
+
+                VNode::Linked(l) => {
+                    todo!()
                 }
                 }
 
 
                 VNode::Component(c) => {
                 VNode::Component(c) => {
                     let scope_id = c.associated_scope.get().unwrap();
                     let scope_id = c.associated_scope.get().unwrap();
-                    let scope = self.vdom.get_scope_mut(scope_id).unwrap();
-                    let root = scope.root_node();
-                    self.remove_nodes(Some(root), gen_muts);
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    let root = self.root_node(&scope_id);
+                    self.remove_nodes(state, Some(root), gen_muts);
 
 
                     log::debug!("Destroying scope {:?}", scope_id);
                     log::debug!("Destroying scope {:?}", scope_id);
-                    let mut s = self.vdom.try_remove(scope_id).unwrap();
+                    let mut s = self.try_remove(&scope_id).unwrap();
                     s.hooks.clear_hooks();
                     s.hooks.clear_hooks();
                 }
                 }
             }
             }
@@ -1147,38 +1267,44 @@ impl<'bump> DiffMachine<'bump> {
     ///
     ///
     /// The new nodes *will* be created - don't create them yourself!
     /// The new nodes *will* be created - don't create them yourself!
     fn replace_and_create_many_with_many(
     fn replace_and_create_many_with_many(
-        &mut self,
+        &'bump self,
+        state: &mut DiffState<'bump>,
         old: &'bump [VNode<'bump>],
         old: &'bump [VNode<'bump>],
         new: &'bump [VNode<'bump>],
         new: &'bump [VNode<'bump>],
     ) {
     ) {
         if let Some(first_old) = old.get(0) {
         if let Some(first_old) = old.get(0) {
-            self.remove_nodes(&old[1..], true);
-            self.stack
+            self.remove_nodes(state, &old[1..], true);
+            state
+                .stack
                 .create_children(new, MountType::Replace { old: first_old })
                 .create_children(new, MountType::Replace { old: first_old })
         } else {
         } else {
-            self.stack.create_children(new, MountType::Append {});
+            state.stack.create_children(new, MountType::Append {});
         }
         }
     }
     }
 
 
     /// Adds a listener closure to a scope during diff.
     /// Adds a listener closure to a scope during diff.
-    fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &ScopeInner) {
-        let mut queue = scope.listeners.borrow_mut();
-        let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
-        queue.push(long_listener as *const _)
+    fn attach_listener_to_scope(&'bump self, listener: &'bump Listener<'bump>, scope: &Scope) {
+        let long_listener = unsafe { std::mem::transmute(listener) };
+        scope.items.borrow_mut().listeners.push(long_listener)
     }
     }
 
 
-    fn attach_suspended_node_to_scope(&mut self, suspended: &'bump VSuspended) {
-        if let Some(scope) = self
+    fn attach_suspended_node_to_scope(
+        &'bump self,
+        state: &mut DiffState<'bump>,
+        suspended: &'bump VSuspended,
+    ) {
+        if let Some(scope) = state
             .stack
             .stack
             .current_scope()
             .current_scope()
-            .and_then(|id| self.vdom.get_scope_mut(id))
+            .and_then(|id| self.get_scope(&id))
         {
         {
             // safety: this lifetime is managed by the logic on scope
             // safety: this lifetime is managed by the logic on scope
-            let extended: &VSuspended<'static> = unsafe { std::mem::transmute(suspended) };
+            let extended = unsafe { std::mem::transmute(suspended) };
             scope
             scope
-                .suspended_nodes
+                .items
                 .borrow_mut()
                 .borrow_mut()
-                .insert(suspended.task_id, extended as *const _);
+                .suspended_nodes
+                .insert(suspended.task_id, extended);
         }
         }
     }
     }
 }
 }

+ 0 - 4
packages/core/src/diff_stack.rs

@@ -49,10 +49,6 @@ impl<'bump> DiffStack<'bump> {
         }
         }
     }
     }
 
 
-    pub fn is_empty(&self) -> bool {
-        self.instructions.is_empty()
-    }
-
     pub fn pop(&mut self) -> Option<DiffInstruction<'bump>> {
     pub fn pop(&mut self) -> Option<DiffInstruction<'bump>> {
         self.instructions.pop()
         self.instructions.pop()
     }
     }

+ 0 - 50
packages/core/src/heuristics.rs

@@ -1,50 +0,0 @@
-use std::collections::HashMap;
-
-use fxhash::FxHashMap;
-
-use crate::FC;
-
-/// Provides heuristics to the "SharedResources" object for improving allocation performance.
-///
-/// This heuristics engine records the memory footprint of bump arenas and hook lists for each component. These records are
-/// then used later on to optimize the initial allocation for future components. This helps save large allocations later on
-/// that would slow down the diffing and initialization process.
-///
-///
-pub struct HeuristicsEngine {
-    heuristics: FxHashMap<FcSlot, Heuristic>,
-}
-
-pub type FcSlot = *const ();
-
-pub struct Heuristic {
-    hooks: u32,
-    bump_size: u64,
-}
-
-impl HeuristicsEngine {
-    pub(crate) fn new() -> Self {
-        Self {
-            heuristics: FxHashMap::default(),
-        }
-    }
-
-    fn recommend<T>(&mut self, fc: FC<T>, heuristic: Heuristic) {
-        let g = fc as FcSlot;
-        let e = self.heuristics.entry(g);
-    }
-
-    fn get_recommendation<T>(&mut self, fc: FC<T>) -> &Heuristic {
-        let id = fc as FcSlot;
-
-        self.heuristics.entry(id).or_insert(Heuristic {
-            bump_size: 100,
-            hooks: 10,
-        })
-    }
-}
-
-#[test]
-fn types_work() {
-    let engine = HeuristicsEngine::new();
-}

+ 20 - 15
packages/core/src/hooklist.rs

@@ -1,10 +1,9 @@
 use std::{
 use std::{
     any::Any,
     any::Any,
-    cell::{Cell, RefCell, UnsafeCell},
+    cell::{Cell, RefCell},
 };
 };
 
 
-type UnsafeInnerHookState = UnsafeCell<Box<dyn Any>>;
-type HookCleanup = Box<dyn FnOnce(Box<dyn Any>)>;
+use bumpalo::Bump;
 
 
 /// An abstraction over internally stored data using a hook-based memory layout.
 /// An abstraction over internally stored data using a hook-based memory layout.
 ///
 ///
@@ -15,7 +14,8 @@ type HookCleanup = Box<dyn FnOnce(Box<dyn Any>)>;
 /// Todo: this could use its very own bump arena, but that might be a tad overkill
 /// Todo: this could use its very own bump arena, but that might be a tad overkill
 #[derive(Default)]
 #[derive(Default)]
 pub(crate) struct HookList {
 pub(crate) struct HookList {
-    vals: RefCell<Vec<(UnsafeInnerHookState, HookCleanup)>>,
+    arena: Bump,
+    vals: RefCell<Vec<*mut dyn Any>>,
     idx: Cell<usize>,
     idx: Cell<usize>,
 }
 }
 
 
@@ -23,7 +23,7 @@ impl HookList {
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {
             self.idx.set(self.idx.get() + 1);
             self.idx.set(self.idx.get() + 1);
-            let raw_box = unsafe { &mut *inn.0.get() };
+            let raw_box = unsafe { &mut **inn };
             raw_box.downcast_mut::<T>()
             raw_box.downcast_mut::<T>()
         })
         })
     }
     }
@@ -34,14 +34,13 @@ impl HookList {
     ///
     ///
     /// This should only be ran by Dioxus itself before "running scope".
     /// This should only be ran by Dioxus itself before "running scope".
     /// Dioxus knows how to descend through the tree to prevent mutable aliasing.
     /// Dioxus knows how to descend through the tree to prevent mutable aliasing.
-    pub(crate) unsafe fn reset(&mut self) {
+    pub(crate) unsafe fn reset(&self) {
         self.idx.set(0);
         self.idx.set(0);
     }
     }
 
 
-    pub(crate) fn push_hook<T: 'static>(&self, new: T, cleanup: HookCleanup) {
-        self.vals
-            .borrow_mut()
-            .push((UnsafeCell::new(Box::new(new)), cleanup))
+    pub(crate) fn push_hook<T: 'static>(&self, new: T) {
+        let val = self.arena.alloc(new);
+        self.vals.borrow_mut().push(val)
     }
     }
 
 
     pub(crate) fn len(&self) -> usize {
     pub(crate) fn len(&self) -> usize {
@@ -57,10 +56,16 @@ impl HookList {
     }
     }
 
 
     pub fn clear_hooks(&mut self) {
     pub fn clear_hooks(&mut self) {
-        log::debug!("clearing hooks...");
-        self.vals
-            .borrow_mut()
-            .drain(..)
-            .for_each(|(state, cleanup)| cleanup(state.into_inner()));
+        self.vals.borrow_mut().drain(..).for_each(|state| {
+            let as_mut = unsafe { &mut *state };
+            let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
+            drop(boxed);
+        });
+    }
+
+    /// Get the ammount of memory a hooklist uses
+    /// Used in heuristics
+    pub fn get_hook_arena_size(&self) -> usize {
+        self.arena.allocated_bytes()
     }
     }
 }
 }

+ 165 - 73
packages/core/src/lazynodes.rs

@@ -27,89 +27,118 @@ pub struct LazyNodes<'a, 'b> {
     inner: StackNodeStorage<'a, 'b>,
     inner: StackNodeStorage<'a, 'b>,
 }
 }
 
 
-type StackHeapSize = [usize; 0];
+type StackHeapSize = [usize; 8];
 
 
 enum StackNodeStorage<'a, 'b> {
 enum StackNodeStorage<'a, 'b> {
     Stack(LazyStack),
     Stack(LazyStack),
-    Heap(Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b>),
+    Heap(Box<dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b>),
 }
 }
 
 
 impl<'a, 'b> LazyNodes<'a, 'b> {
 impl<'a, 'b> LazyNodes<'a, 'b> {
-    pub fn new<F>(val: F) -> Self
+    pub fn new<F>(_val: F) -> Self
     where
     where
         F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
         F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
     {
     {
-        unsafe {
-            let mut ptr: *const _ = &val as &dyn FnOnce(NodeFactory<'a>) -> VNode<'a>;
+        // there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
+        let mut slot = Some(_val);
 
 
-            assert_eq!(
-                ptr as *const u8, &val as *const _ as *const u8,
-                "MISUSE: Closure returned different pointer"
-            );
-            assert_eq!(
-                std::mem::size_of_val(&*ptr),
-                std::mem::size_of::<F>(),
-                "MISUSE: Closure returned a subset pointer"
-            );
-            let words = ptr_as_slice(&mut ptr);
-            assert!(
-                words[0] == &val as *const _ as usize,
-                "BUG: Pointer layout is not (data_ptr, info...)"
-            );
+        let val = move |fac: Option<NodeFactory<'a>>| {
+            let inner = slot.take().unwrap();
+            fac.map(inner)
+        };
 
 
-            // - Ensure that Self is aligned same as data requires
-            assert!(
-                std::mem::align_of::<F>() <= std::mem::align_of::<Self>(),
-                "TODO: Enforce alignment >{} (requires {})",
-                std::mem::align_of::<Self>(),
-                std::mem::align_of::<F>()
-            );
+        unsafe { LazyNodes::new_inner(val) }
+    }
 
 
-            let info = &words[1..];
-            let data = words[0] as *mut ();
-            let size = mem::size_of::<F>();
+    unsafe fn new_inner<F>(val: F) -> Self
+    where
+        F: FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b,
+    {
+        let mut ptr: *const _ = &val as &dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>>;
+
+        assert_eq!(
+            ptr as *const u8, &val as *const _ as *const u8,
+            "MISUSE: Closure returned different pointer"
+        );
+        assert_eq!(
+            std::mem::size_of_val(&*ptr),
+            std::mem::size_of::<F>(),
+            "MISUSE: Closure returned a subset pointer"
+        );
+
+        let words = ptr_as_slice(&mut ptr);
+        assert!(
+            words[0] == &val as *const _ as usize,
+            "BUG: Pointer layout is not (data_ptr, info...)"
+        );
+
+        // - Ensure that Self is aligned same as data requires
+        assert!(
+            std::mem::align_of::<F>() <= std::mem::align_of::<Self>(),
+            "TODO: Enforce alignment >{} (requires {})",
+            std::mem::align_of::<Self>(),
+            std::mem::align_of::<F>()
+        );
+
+        let info = &words[1..];
+        let data = words[0] as *mut ();
+        let size = mem::size_of::<F>();
+
+        let stored_size = info.len() * mem::size_of::<usize>() + size;
+        let max_size = mem::size_of::<StackHeapSize>();
+
+        if stored_size > max_size {
+            log::debug!(
+                    "lazy nodes was too large to fit into stack. falling back to heap. max: {}, actual {:?}",
+                    max_size,
+                    stored_size
+                );
+
+            Self {
+                inner: StackNodeStorage::Heap(Box::new(val)),
+            }
+        } else {
+            log::debug!(
+                "lazy nodes fits on stack! max: {}, actual: {:?}",
+                max_size,
+                stored_size
+            );
+            let mut buf: StackHeapSize = StackHeapSize::default();
 
 
-            if info.len() * mem::size_of::<usize>() + size > mem::size_of::<StackHeapSize>() {
-                log::debug!("lazy nodes was too large to fit into stack. falling back to heap");
+            assert!(info.len() + round_to_words(size) <= buf.as_ref().len());
 
 
-                Self {
-                    inner: StackNodeStorage::Heap(Box::new(val)),
-                }
-            } else {
-                log::debug!("lazy nodes fits on stack!");
-                let mut buf: StackHeapSize = StackHeapSize::default();
-
-                assert!(info.len() + round_to_words(size) <= buf.as_ref().len());
-
-                // Place pointer information at the end of the region
-                // - Allows the data to be at the start for alignment purposes
-                {
-                    let info_ofs = buf.as_ref().len() - info.len();
-                    let info_dst = &mut buf.as_mut()[info_ofs..];
-                    for (d, v) in Iterator::zip(info_dst.iter_mut(), info.iter()) {
-                        *d = *v;
-                    }
+            // Place pointer information at the end of the region
+            // - Allows the data to be at the start for alignment purposes
+            {
+                let info_ofs = buf.as_ref().len() - info.len();
+                let info_dst = &mut buf.as_mut()[info_ofs..];
+                for (d, v) in Iterator::zip(info_dst.iter_mut(), info.iter()) {
+                    *d = *v;
                 }
                 }
+            }
 
 
-                let src_ptr = data as *const u8;
-                let dataptr = buf.as_mut()[..].as_mut_ptr() as *mut u8;
-                for i in 0..size {
-                    *dataptr.add(i) = *src_ptr.add(i);
-                }
+            let src_ptr = data as *const u8;
+            let dataptr = buf.as_mut()[..].as_mut_ptr() as *mut u8;
+            for i in 0..size {
+                *dataptr.add(i) = *src_ptr.add(i);
+            }
 
 
-                std::mem::forget(val);
+            std::mem::forget(val);
 
 
-                Self {
-                    inner: StackNodeStorage::Stack(LazyStack { _align: [], buf }),
-                }
+            Self {
+                inner: StackNodeStorage::Stack(LazyStack {
+                    _align: [],
+                    buf,
+                    dropped: false,
+                }),
             }
             }
         }
         }
     }
     }
 
 
     pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
     pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
         match self.inner {
         match self.inner {
-            StackNodeStorage::Heap(lazy) => lazy(f),
-            StackNodeStorage::Stack(stack) => stack.call(f),
+            StackNodeStorage::Heap(mut lazy) => lazy(Some(f)).unwrap(),
+            StackNodeStorage::Stack(mut stack) => stack.call(f),
         }
         }
     }
     }
 }
 }
@@ -117,34 +146,51 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
 struct LazyStack {
 struct LazyStack {
     _align: [u64; 0],
     _align: [u64; 0],
     buf: StackHeapSize,
     buf: StackHeapSize,
+    dropped: bool,
 }
 }
 
 
 impl LazyStack {
 impl LazyStack {
-    unsafe fn create_boxed<'a>(&mut self) -> Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>> {
+    fn call<'a>(&mut self, f: NodeFactory<'a>) -> VNode<'a> {
         let LazyStack { buf, .. } = self;
         let LazyStack { buf, .. } = self;
         let data = buf.as_ref();
         let data = buf.as_ref();
 
 
-        let info_size = mem::size_of::<*mut dyn FnOnce(NodeFactory<'a>) -> VNode<'a>>()
-            / mem::size_of::<usize>()
-            - 1;
+        let info_size =
+            mem::size_of::<*mut dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>>>()
+                / mem::size_of::<usize>()
+                - 1;
 
 
         let info_ofs = data.len() - info_size;
         let info_ofs = data.len() - info_size;
 
 
-        let g: *mut dyn FnOnce(NodeFactory<'a>) -> VNode<'a> =
-            make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]);
+        let g: *mut dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> =
+            unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
 
 
-        Box::from_raw(g)
-    }
+        self.dropped = true;
 
 
-    fn call(mut self, f: NodeFactory) -> VNode {
-        let boxed = unsafe { self.create_boxed() };
-        boxed(f)
+        let clos = unsafe { &mut *g };
+        clos(Some(f)).unwrap()
     }
     }
 }
 }
 impl Drop for LazyStack {
 impl Drop for LazyStack {
     fn drop(&mut self) {
     fn drop(&mut self) {
-        let boxed = unsafe { self.create_boxed() };
-        mem::drop(boxed);
+        if !self.dropped {
+            let LazyStack { buf, .. } = self;
+            let data = buf.as_ref();
+
+            let info_size = mem::size_of::<
+                *mut dyn FnMut(Option<NodeFactory<'_>>) -> Option<VNode<'_>>,
+            >() / mem::size_of::<usize>()
+                - 1;
+
+            let info_ofs = data.len() - info_size;
+
+            let g: *mut dyn FnMut(Option<NodeFactory<'_>>) -> Option<VNode<'_>> =
+                unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
+
+            self.dropped = true;
+
+            let clos = unsafe { &mut *g };
+            clos(None);
+        }
     }
     }
 }
 }
 
 
@@ -177,7 +223,7 @@ fn round_to_words(len: usize) -> usize {
 fn it_works() {
 fn it_works() {
     let bump = bumpalo::Bump::new();
     let bump = bumpalo::Bump::new();
 
 
-    simple_logger::init();
+    simple_logger::init().unwrap();
 
 
     let factory = NodeFactory { bump: &bump };
     let factory = NodeFactory { bump: &bump };
 
 
@@ -191,3 +237,49 @@ fn it_works() {
 
 
     dbg!(g);
     dbg!(g);
 }
 }
+
+#[test]
+fn it_drops() {
+    use std::rc::Rc;
+    let bump = bumpalo::Bump::new();
+
+    simple_logger::init().unwrap();
+
+    let factory = NodeFactory { bump: &bump };
+
+    struct DropInner {
+        id: i32,
+    }
+    impl Drop for DropInner {
+        fn drop(&mut self) {
+            log::debug!("dropping inner");
+        }
+    }
+    let val = Rc::new(10);
+
+    {
+        let it = (0..10)
+            .map(|i| {
+                let val = val.clone();
+
+                NodeFactory::annotate_lazy(move |f| {
+                    log::debug!("hell closure");
+                    let inner = DropInner { id: i };
+                    f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
+                })
+            })
+            .collect::<Vec<_>>();
+
+        let caller = NodeFactory::annotate_lazy(|f| {
+            log::debug!("main closure");
+            f.fragment_from_iter(it)
+        })
+        .unwrap();
+    }
+
+    // let nodes = caller.call(factory);
+
+    // dbg!(nodes);
+
+    dbg!(Rc::strong_count(&val));
+}

+ 11 - 30
packages/core/src/lib.rs

@@ -12,68 +12,49 @@ Navigating this crate:
 
 
 Some utilities
 Some utilities
 */
 */
-pub(crate) mod bumpframe;
-pub(crate) mod childiter;
 pub(crate) mod component;
 pub(crate) mod component;
-pub(crate) mod coroutines;
 pub(crate) mod diff;
 pub(crate) mod diff;
 pub(crate) mod diff_stack;
 pub(crate) mod diff_stack;
-pub(crate) mod events;
-pub(crate) mod heuristics;
 pub(crate) mod hooklist;
 pub(crate) mod hooklist;
-pub(crate) mod hooks;
 pub(crate) mod lazynodes;
 pub(crate) mod lazynodes;
 pub(crate) mod mutations;
 pub(crate) mod mutations;
 pub(crate) mod nodes;
 pub(crate) mod nodes;
-pub(crate) mod resources;
-pub(crate) mod scheduler;
 pub(crate) mod scope;
 pub(crate) mod scope;
-pub(crate) mod tasks;
+pub(crate) mod scopearena;
 pub(crate) mod test_dom;
 pub(crate) mod test_dom;
-pub(crate) mod threadsafe;
 pub(crate) mod util;
 pub(crate) mod util;
 pub(crate) mod virtual_dom;
 pub(crate) mod virtual_dom;
 
 
-#[cfg(feature = "debug_vdom")]
-pub mod debug_dom;
-
 pub(crate) mod innerlude {
 pub(crate) mod innerlude {
-    pub(crate) use crate::bumpframe::*;
-    pub(crate) use crate::childiter::*;
     pub use crate::component::*;
     pub use crate::component::*;
     pub(crate) use crate::diff::*;
     pub(crate) use crate::diff::*;
     pub use crate::diff_stack::*;
     pub use crate::diff_stack::*;
-    pub use crate::events::*;
-    pub use crate::heuristics::*;
     pub(crate) use crate::hooklist::*;
     pub(crate) use crate::hooklist::*;
-    pub use crate::hooks::*;
     pub use crate::lazynodes::*;
     pub use crate::lazynodes::*;
     pub use crate::mutations::*;
     pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub use crate::nodes::*;
-    pub(crate) use crate::resources::*;
-    pub use crate::scheduler::*;
     pub use crate::scope::*;
     pub use crate::scope::*;
-    pub use crate::tasks::*;
+    pub use crate::scopearena::*;
     pub use crate::test_dom::*;
     pub use crate::test_dom::*;
-    pub use crate::threadsafe::*;
     pub use crate::util::*;
     pub use crate::util::*;
     pub use crate::virtual_dom::*;
     pub use crate::virtual_dom::*;
 
 
-    pub type Element<'a> = Option<VNode<'a>>;
-    pub type FC<P> = for<'a> fn(Scope<'a, P>) -> Element<'a>;
+    pub type Element = Option<NodeLink>;
+    pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> Element;
 }
 }
 
 
 pub use crate::innerlude::{
 pub use crate::innerlude::{
-    Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, LazyNodes, MountType,
-    Mutations, NodeFactory, Properties, SchedulerMsg, ScopeChildren, ScopeId, TaskHandle, TestDom,
-    ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC,
+    Attribute, Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, LazyNodes,
+    Listener, MountType, Mutations, NodeFactory, Properties, SchedulerMsg, ScopeChildren, ScopeId,
+    TestDom, UserEvent, VAnchor, VElement, VFragment, VNode, VSuspended, VirtualDom, FC,
 };
 };
 
 
 pub mod prelude {
 pub mod prelude {
-    pub use crate::component::{fc_to_builder, Fragment, Properties, Scope};
-    pub use crate::hooks::*;
+    pub use crate::component::{fc_to_builder, Fragment, Properties};
     pub use crate::innerlude::Context;
     pub use crate::innerlude::Context;
-    pub use crate::innerlude::{DioxusElement, Element, LazyNodes, NodeFactory, ScopeChildren, FC};
+    pub use crate::innerlude::{
+        DioxusElement, Element, LazyNodes, NodeFactory, Scope, ScopeChildren, FC,
+    };
     pub use crate::nodes::VNode;
     pub use crate::nodes::VNode;
     pub use crate::VirtualDom;
     pub use crate::VirtualDom;
 }
 }

+ 115 - 43
packages/core/src/nodes.rs

@@ -4,7 +4,7 @@
 //! cheap and *very* fast to construct - building a full tree should be quick.
 //! cheap and *very* fast to construct - building a full tree should be quick.
 
 
 use crate::{
 use crate::{
-    innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId, ScopeInner},
+    innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId},
     lazynodes::LazyNodes,
     lazynodes::LazyNodes,
 };
 };
 use bumpalo::{boxed::Box as BumpBox, Bump};
 use bumpalo::{boxed::Box as BumpBox, Bump};
@@ -14,6 +14,19 @@ use std::{
     fmt::{Arguments, Debug, Formatter},
     fmt::{Arguments, Debug, Formatter},
 };
 };
 
 
+/// A cached node is a "pointer" to a "rendered" node in a particular scope
+///
+/// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
+///
+/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
+/// is essentially a unique key to guarantee safe usage of the Node.
+#[derive(Clone, Debug)]
+pub struct NodeLink {
+    pub(crate) link_idx: usize,
+    pub(crate) gen_id: u32,
+    pub(crate) scope_id: ScopeId,
+}
+
 /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
 /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
 ///
 ///
 /// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
 /// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
@@ -118,6 +131,13 @@ pub enum VNode<'src> {
     /// }
     /// }
     /// ```
     /// ```
     Anchor(&'src VAnchor),
     Anchor(&'src VAnchor),
+
+    /// A type of node that links this node to another scope or render cycle
+    ///
+    /// Is essentially a "pointer" to a "rendered" node in a particular scope
+    ///
+    /// Used in portals
+    Linked(NodeLink),
 }
 }
 
 
 impl<'src> VNode<'src> {
 impl<'src> VNode<'src> {
@@ -127,9 +147,11 @@ impl<'src> VNode<'src> {
             VNode::Element(el) => el.key,
             VNode::Element(el) => el.key,
             VNode::Component(c) => c.key,
             VNode::Component(c) => c.key,
             VNode::Fragment(f) => f.key,
             VNode::Fragment(f) => f.key,
+
             VNode::Text(_t) => None,
             VNode::Text(_t) => None,
             VNode::Suspended(_s) => None,
             VNode::Suspended(_s) => None,
             VNode::Anchor(_f) => None,
             VNode::Anchor(_f) => None,
+            VNode::Linked(_c) => None,
         }
         }
     }
     }
 
 
@@ -149,11 +171,20 @@ impl<'src> VNode<'src> {
             VNode::Element(el) => el.dom_id.get(),
             VNode::Element(el) => el.dom_id.get(),
             VNode::Anchor(el) => el.dom_id.get(),
             VNode::Anchor(el) => el.dom_id.get(),
             VNode::Suspended(el) => el.dom_id.get(),
             VNode::Suspended(el) => el.dom_id.get(),
+            VNode::Linked(_) => None,
             VNode::Fragment(_) => None,
             VNode::Fragment(_) => None,
             VNode::Component(_) => None,
             VNode::Component(_) => None,
         }
         }
     }
     }
 
 
+    pub fn children(&self) -> &[VNode<'src>] {
+        match &self {
+            VNode::Fragment(f) => f.children,
+            VNode::Component(c) => todo!("children are not accessible through this"),
+            _ => &[],
+        }
+    }
+
     // Create an "owned" version of the vnode.
     // Create an "owned" version of the vnode.
     pub fn decouple(&self) -> VNode<'src> {
     pub fn decouple(&self) -> VNode<'src> {
         match self {
         match self {
@@ -165,7 +196,11 @@ impl<'src> VNode<'src> {
             VNode::Fragment(f) => VNode::Fragment(VFragment {
             VNode::Fragment(f) => VNode::Fragment(VFragment {
                 children: f.children,
                 children: f.children,
                 key: f.key,
                 key: f.key,
-                is_static: f.is_static,
+            }),
+            VNode::Linked(c) => VNode::Linked(NodeLink {
+                gen_id: c.gen_id,
+                scope_id: c.scope_id,
+                link_idx: c.link_idx,
             }),
             }),
         }
         }
     }
     }
@@ -175,17 +210,24 @@ impl Debug for VNode<'_> {
     fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
     fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
         match &self {
         match &self {
             VNode::Element(el) => s
             VNode::Element(el) => s
-                .debug_struct("VElement")
+                .debug_struct("VNode::VElement")
                 .field("name", &el.tag_name)
                 .field("name", &el.tag_name)
                 .field("key", &el.key)
                 .field("key", &el.key)
                 .finish(),
                 .finish(),
 
 
-            VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
-            VNode::Anchor(_) => write!(s, "VAnchor"),
+            VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
+            VNode::Anchor(_) => write!(s, "VNode::VAnchor"),
 
 
-            VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
-            VNode::Suspended { .. } => write!(s, "VSuspended"),
-            VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
+            VNode::Fragment(frag) => {
+                write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
+            }
+            VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
+            VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
+            VNode::Linked(c) => write!(
+                s,
+                "VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
+                c.gen_id, c.scope_id
+            ),
         }
         }
     }
     }
 }
 }
@@ -209,8 +251,6 @@ pub struct VFragment<'src> {
     pub key: Option<&'src str>,
     pub key: Option<&'src str>,
 
 
     pub children: &'src [VNode<'src>],
     pub children: &'src [VNode<'src>],
-
-    pub is_static: bool,
 }
 }
 
 
 /// An element like a "div" with children, listeners, and attributes.
 /// An element like a "div" with children, listeners, and attributes.
@@ -309,6 +349,7 @@ pub struct Listener<'bump> {
     pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(Box<dyn Any + Send>) + 'bump>>>,
     pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(Box<dyn Any + Send>) + 'bump>>>,
 }
 }
 
 
+pub type VCompCaller<'src> = BumpBox<'src, dyn Fn(Context) -> Element + 'src>;
 /// Virtual Components for custom user-defined components
 /// Virtual Components for custom user-defined components
 /// Only supports the functional syntax
 /// Only supports the functional syntax
 pub struct VComponent<'src> {
 pub struct VComponent<'src> {
@@ -316,21 +357,19 @@ pub struct VComponent<'src> {
 
 
     pub associated_scope: Cell<Option<ScopeId>>,
     pub associated_scope: Cell<Option<ScopeId>>,
 
 
-    pub is_static: bool,
-
     // Function pointer to the FC that was used to generate this component
     // Function pointer to the FC that was used to generate this component
     pub user_fc: *const (),
     pub user_fc: *const (),
 
 
-    pub(crate) caller: BumpBox<'src, dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> + 'src>,
-
-    pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
-
-    pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
-
     pub(crate) can_memoize: bool,
     pub(crate) can_memoize: bool,
 
 
+    pub(crate) hard_allocation: Cell<Option<*const ()>>,
+
     // Raw pointer into the bump arena for the props of the component
     // Raw pointer into the bump arena for the props of the component
-    pub(crate) raw_props: *const (),
+    pub(crate) bump_props: *const (),
+
+    // during the "teardown" process we'll take the caller out so it can be dropped properly
+    pub(crate) caller: Option<VCompCaller<'src>>,
+    pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
 }
 }
 
 
 pub struct VSuspended<'a> {
 pub struct VSuspended<'a> {
@@ -338,7 +377,7 @@ pub struct VSuspended<'a> {
     pub dom_id: Cell<Option<ElementId>>,
     pub dom_id: Cell<Option<ElementId>>,
 
 
     #[allow(clippy::type_complexity)]
     #[allow(clippy::type_complexity)]
-    pub callback: RefCell<Option<BumpBox<'a, dyn FnMut() -> Element<'a> + 'a>>>,
+    pub callback: RefCell<Option<BumpBox<'a, dyn FnMut() -> Element + 'a>>>,
 }
 }
 
 
 /// This struct provides an ergonomic API to quickly build VNodes.
 /// This struct provides an ergonomic API to quickly build VNodes.
@@ -475,16 +514,30 @@ impl<'a> NodeFactory<'a> {
 
 
     pub fn component<P>(
     pub fn component<P>(
         &self,
         &self,
-        component: fn(Scope<'a, P>) -> Element<'a>,
+        component: fn(Context<'a>, &'a P) -> Element,
         props: P,
         props: P,
         key: Option<Arguments>,
         key: Option<Arguments>,
     ) -> VNode<'a>
     ) -> VNode<'a>
     where
     where
         P: Properties + 'a,
         P: Properties + 'a,
     {
     {
+        /*
+        our strategy:
+        - unsafe hackery
+        - lol
+
+        - we don't want to hit the global allocator
+        - allocate into our bump arena
+        - if the props aren't static, then we convert them into a box which we pass off between renders
+        */
+
+        // let bump = self.bump();
+
+        // // later, we'll do a hard allocation
+        // let raw_ptr = bump.alloc(props);
         let bump = self.bump();
         let bump = self.bump();
         let props = bump.alloc(props);
         let props = bump.alloc(props);
-        let raw_props = props as *mut P as *mut ();
+        let bump_props = props as *mut P as *mut ();
         let user_fc = component as *const ();
         let user_fc = component as *const ();
 
 
         let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
         let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
@@ -496,7 +549,7 @@ impl<'a> NodeFactory<'a> {
                     // - Non-static P are autoderived to memoize as false
                     // - Non-static P are autoderived to memoize as false
                     // - This comparator is only called on a corresponding set of bumpframes
                     // - This comparator is only called on a corresponding set of bumpframes
                     let props_memoized = unsafe {
                     let props_memoized = unsafe {
-                        let real_other: &P = &*(other.raw_props as *const _ as *const P);
+                        let real_other: &P = &*(other.bump_props as *const _ as *const P);
                         props.memoize(real_other)
                         props.memoize(real_other)
                     };
                     };
 
 
@@ -508,7 +561,6 @@ impl<'a> NodeFactory<'a> {
                 }
                 }
             }
             }
         });
         });
-        let comparator = Some(unsafe { BumpBox::from_raw(comparator) });
 
 
         let drop_props = {
         let drop_props = {
             // create a closure to drop the props
             // create a closure to drop the props
@@ -518,7 +570,7 @@ impl<'a> NodeFactory<'a> {
                 move || unsafe {
                 move || unsafe {
                     log::debug!("dropping props!");
                     log::debug!("dropping props!");
                     if !has_dropped {
                     if !has_dropped {
-                        let real_other = raw_props as *mut _ as *mut P;
+                        let real_other = bump_props as *mut _ as *mut P;
                         let b = BumpBox::from_raw(real_other);
                         let b = BumpBox::from_raw(real_other);
                         std::mem::drop(b);
                         std::mem::drop(b);
 
 
@@ -536,33 +588,41 @@ impl<'a> NodeFactory<'a> {
 
 
         let key = key.map(|f| self.raw_text(f).0);
         let key = key.map(|f| self.raw_text(f).0);
 
 
-        let caller: &'a mut dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> =
-            bump.alloc(move |scope: &ScopeInner| -> Element {
+        let caller: &'a mut dyn Fn(&Scope) -> Element =
+            bump.alloc(move |scope: &Scope| -> Element {
                 log::debug!("calling component renderr {:?}", scope.our_arena_idx);
                 log::debug!("calling component renderr {:?}", scope.our_arena_idx);
-                let props: &'_ P = unsafe { &*(raw_props as *const P) };
-
-                let scp: &'a ScopeInner = unsafe { std::mem::transmute(scope) };
-                let s: Scope<'a, P> = (scp, props);
-
-                let res: Element = component(s);
+                let props: &'_ P = unsafe { &*(bump_props as *const P) };
+                let res = component(scope, props);
+                // let res = component((Context { scope }, props));
                 unsafe { std::mem::transmute(res) }
                 unsafe { std::mem::transmute(res) }
             });
             });
 
 
-        let caller = unsafe { BumpBox::from_raw(caller) };
+        let can_memoize = P::IS_STATIC;
 
 
         VNode::Component(bump.alloc(VComponent {
         VNode::Component(bump.alloc(VComponent {
             user_fc,
             user_fc,
             comparator,
             comparator,
-            raw_props,
+            bump_props,
             caller,
             caller,
-            is_static: P::IS_STATIC,
             key,
             key,
-            can_memoize: P::IS_STATIC,
+            can_memoize,
             drop_props,
             drop_props,
             associated_scope: Cell::new(None),
             associated_scope: Cell::new(None),
         }))
         }))
     }
     }
 
 
+    pub fn listener(
+        self,
+        event: &'static str,
+        callback: BumpBox<'a, dyn FnMut(Box<dyn Any + Send>) + 'a>,
+    ) -> Listener<'a> {
+        Listener {
+            mounted_node: Cell::new(None),
+            event,
+            callback: RefCell::new(Some(callback)),
+        }
+    }
+
     pub fn fragment_from_iter(
     pub fn fragment_from_iter(
         self,
         self,
         node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
         node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
@@ -604,14 +664,12 @@ impl<'a> NodeFactory<'a> {
         VNode::Fragment(VFragment {
         VNode::Fragment(VFragment {
             children,
             children,
             key: None,
             key: None,
-            is_static: false,
         })
         })
     }
     }
 
 
-    pub fn annotate_lazy<'z, 'b, F>(f: F) -> Option<LazyNodes<'z, 'b>>
-    where
-        F: FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b,
-    {
+    pub fn annotate_lazy<'z, 'b>(
+        f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b,
+    ) -> Option<LazyNodes<'z, 'b>> {
         Some(LazyNodes::new(f))
         Some(LazyNodes::new(f))
     }
     }
 }
 }
@@ -672,6 +730,21 @@ impl IntoVNode<'_> for Option<()> {
     }
     }
 }
 }
 
 
+// Conveniently, we also support "None"
+impl IntoVNode<'_> for Option<NodeLink> {
+    fn into_vnode(self, cx: NodeFactory) -> VNode {
+        todo!()
+        // cx.fragment_from_iter(None as Option<VNode>)
+    }
+}
+// Conveniently, we also support "None"
+impl IntoVNode<'_> for NodeLink {
+    fn into_vnode(self, cx: NodeFactory) -> VNode {
+        todo!()
+        // cx.fragment_from_iter(None as Option<VNode>)
+    }
+}
+
 impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
 impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
     fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
     fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
         self.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
         self.unwrap_or_else(|| cx.fragment_from_iter(None as Option<VNode>))
@@ -684,7 +757,6 @@ impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
             Some(lazy) => lazy.call(cx),
             Some(lazy) => lazy.call(cx),
             None => VNode::Fragment(VFragment {
             None => VNode::Fragment(VFragment {
                 children: &[],
                 children: &[],
-                is_static: false,
                 key: None,
                 key: None,
             }),
             }),
         }
         }

+ 27 - 45
packages/core/src/bumpframe.rs → packages/core/src/old/bumpframe.rs

@@ -3,69 +3,51 @@ use bumpalo::Bump;
 use std::cell::Cell;
 use std::cell::Cell;
 
 
 pub(crate) struct ActiveFrame {
 pub(crate) struct ActiveFrame {
-    // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
-    pub generation: Cell<usize>,
+    pub cur_generation: Cell<usize>,
 
 
     // The double-buffering situation that we will use
     // The double-buffering situation that we will use
     pub frames: [BumpFrame; 2],
     pub frames: [BumpFrame; 2],
 }
 }
 
 
-pub(crate) struct BumpFrame {
-    pub bump: Bump,
-    pub(crate) head_node: VNode<'static>,
-
-    #[cfg(test)]
-    // used internally for debugging
-    _name: &'static str,
-}
-
 impl ActiveFrame {
 impl ActiveFrame {
     pub fn new() -> Self {
     pub fn new() -> Self {
         let b1 = Bump::new();
         let b1 = Bump::new();
         let b2 = Bump::new();
         let b2 = Bump::new();
 
 
         let frame_a = BumpFrame {
         let frame_a = BumpFrame {
-            head_node: VNode::Fragment(VFragment {
-                key: None,
-                children: &[],
-                is_static: false,
-            }),
             bump: b1,
             bump: b1,
-
-            #[cfg(test)]
-            _name: "wip",
+            generation: 0.into(),
         };
         };
         let frame_b = BumpFrame {
         let frame_b = BumpFrame {
-            head_node: VNode::Fragment(VFragment {
-                key: None,
-                children: &[],
-                is_static: false,
-            }),
             bump: b2,
             bump: b2,
-
-            #[cfg(test)]
-            _name: "fin",
+            generation: 0.into(),
         };
         };
+
         Self {
         Self {
-            generation: 0.into(),
             frames: [frame_a, frame_b],
             frames: [frame_a, frame_b],
+            cur_generation: 0.into(),
         }
         }
     }
     }
 
 
-    pub unsafe fn reset_wip_frame(&mut self) {
-        self.wip_frame_mut().bump.reset()
+    pub unsafe fn reset_wip_frame(&self) {
+        // todo: unsafecell or something
+        let bump = self.wip_frame() as *const _ as *mut BumpFrame;
+        let g = &mut *bump;
+        g.bump.reset();
+
+        // self.wip_frame_mut().bump.reset()
     }
     }
 
 
     /// The "work in progress frame" represents the frame that is currently being worked on.
     /// The "work in progress frame" represents the frame that is currently being worked on.
     pub fn wip_frame(&self) -> &BumpFrame {
     pub fn wip_frame(&self) -> &BumpFrame {
-        match self.generation.get() & 1 == 0 {
+        match self.cur_generation.get() & 1 == 0 {
             true => &self.frames[0],
             true => &self.frames[0],
             false => &self.frames[1],
             false => &self.frames[1],
         }
         }
     }
     }
 
 
     pub fn wip_frame_mut(&mut self) -> &mut BumpFrame {
     pub fn wip_frame_mut(&mut self) -> &mut BumpFrame {
-        match self.generation.get() & 1 == 0 {
+        match self.cur_generation.get() & 1 == 0 {
             true => &mut self.frames[0],
             true => &mut self.frames[0],
             false => &mut self.frames[1],
             false => &mut self.frames[1],
         }
         }
@@ -73,26 +55,26 @@ impl ActiveFrame {
 
 
     /// The finished frame represents the frame that has been "finished" and cannot be modified again
     /// The finished frame represents the frame that has been "finished" and cannot be modified again
     pub fn finished_frame(&self) -> &BumpFrame {
     pub fn finished_frame(&self) -> &BumpFrame {
-        match self.generation.get() & 1 == 1 {
+        match self.cur_generation.get() & 1 == 1 {
             true => &self.frames[0],
             true => &self.frames[0],
             false => &self.frames[1],
             false => &self.frames[1],
         }
         }
     }
     }
 
 
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
-        let cur_head = &self.finished_frame().head_node;
-        unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
-    }
+    // /// Give out our self-referential item with our own borrowed lifetime
+    // pub fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
+    //     let cur_head = &self.finished_frame().head_node;
+    //     unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+    // }
 
 
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
-        let cur_head = &self.wip_frame().head_node;
-        unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
-    }
+    // /// Give out our self-referential item with our own borrowed lifetime
+    // pub fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
+    //     let cur_head = &self.wip_frame().head_node;
+    //     unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+    // }
 
 
     pub fn cycle_frame(&mut self) {
     pub fn cycle_frame(&mut self) {
-        self.generation.set(self.generation.get() + 1);
+        self.cur_generation.set(self.cur_generation.get() + 1);
     }
     }
 }
 }
 
 
@@ -119,6 +101,6 @@ mod tests {
             assert_eq!(fin._name, "wip");
             assert_eq!(fin._name, "wip");
             frames.cycle_frame();
             frames.cycle_frame();
         }
         }
-        assert_eq!(frames.generation.get(), 10);
+        assert_eq!(frames.cur_generation.get(), 10);
     }
     }
 }
 }

+ 1 - 1
packages/core/src/childiter.rs → packages/core/src/old/childiter.rs

@@ -62,7 +62,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
                     VNode::Component(sc) => {
                     VNode::Component(sc) => {
                         let scope = self
                         let scope = self
                             .scopes
                             .scopes
-                            .get_scope(sc.associated_scope.get().unwrap())
+                            .get_scope(&sc.associated_scope.get().unwrap())
                             .unwrap();
                             .unwrap();
 
 
                         // Simply swap the current node on the stack with the root of the component
                         // Simply swap the current node on the stack with the root of the component

+ 6 - 3
packages/core/src/debug_dom.rs → packages/core/src/old/debug_dom.rs

@@ -1,4 +1,4 @@
-use crate::{innerlude::ScopeInner, virtual_dom::VirtualDom, VNode};
+use crate::{innerlude::ScopeState, virtual_dom::VirtualDom, VNode};
 
 
 impl std::fmt::Display for VirtualDom {
 impl std::fmt::Display for VirtualDom {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -24,7 +24,7 @@ impl std::fmt::Display for VirtualDom {
 pub(crate) struct ScopeRenderer<'a> {
 pub(crate) struct ScopeRenderer<'a> {
     pub skip_components: bool,
     pub skip_components: bool,
     pub show_fragments: bool,
     pub show_fragments: bool,
-    pub _scope: &'a ScopeInner,
+    pub _scope: &'a ScopeState,
     pub _pre_render: bool,
     pub _pre_render: bool,
     pub _newline: bool,
     pub _newline: bool,
     pub _indent: bool,
     pub _indent: bool,
@@ -48,6 +48,9 @@ impl<'a> ScopeRenderer<'a> {
         };
         };
 
 
         match &node {
         match &node {
+            VNode::Linked(_) => {
+                write!(f, "Linked").unwrap();
+            }
             VNode::Text(text) => {
             VNode::Text(text) => {
                 write_indent(f, il);
                 write_indent(f, il);
                 writeln!(f, "\"{}\"", text.text)?
                 writeln!(f, "\"{}\"", text.text)?
@@ -115,7 +118,7 @@ impl<'a> ScopeRenderer<'a> {
             VNode::Component(vcomp) => {
             VNode::Component(vcomp) => {
                 let idx = vcomp.associated_scope.get().unwrap();
                 let idx = vcomp.associated_scope.get().unwrap();
                 if !self.skip_components {
                 if !self.skip_components {
-                    let new_node = vdom.get_scope(idx).unwrap().root_node();
+                    let new_node = vdom.get_scope(&idx).unwrap().root_node();
                     self.render(vdom, new_node, f, il)?;
                     self.render(vdom, new_node, f, il)?;
                 }
                 }
             }
             }

+ 15 - 0
packages/core/src/old/events.rs

@@ -0,0 +1,15 @@
+//! An event system that's less confusing than Traits + RC;
+//! This should hopefully make it easier to port to other platforms.
+//!
+//! Unfortunately, it is less efficient than the original, but hopefully it's negligible.
+
+use crate::{
+    innerlude::Listener,
+    innerlude::{ElementId, NodeFactory, ScopeId},
+};
+use bumpalo::boxed::Box as BumpBox;
+use std::{
+    any::Any,
+    cell::{Cell, RefCell},
+    fmt::Debug,
+};

+ 74 - 74
packages/core/src/hooks.rs → packages/core/src/old/hooks.rs

@@ -13,73 +13,73 @@ use crate::innerlude::*;
 use futures_util::FutureExt;
 use futures_util::FutureExt;
 use std::{any::Any, cell::RefCell, future::Future, ops::Deref, rc::Rc};
 use std::{any::Any, cell::RefCell, future::Future, ops::Deref, rc::Rc};
 
 
-/// Awaits the given task, forcing the component to re-render when the value is ready.
-///
-/// Returns the handle to the task and the value (if it is ready, else None).
-///
-/// ```
-/// static Example: FC<()> = |(cx, props)| {
-///     let (task, value) = use_task(|| async {
-///         timer::sleep(Duration::from_secs(1)).await;
-///         "Hello World"
-///     });
-///
-///     match contents {
-///         Some(contents) => rsx!(cx, div { "{title}" }),
-///         None => rsx!(cx, div { "Loading..." }),
-///     }
-/// };
-/// ```
-pub fn use_coroutine<'src, Out, Fut, Init>(
-    cx: Context<'src>,
-    task_initializer: Init,
-) -> (&'src TaskHandle, &'src Option<Out>)
-where
-    Out: 'static,
-    Fut: Future<Output = Out> + 'static,
-    Init: FnOnce() -> Fut + 'src,
-{
-    struct TaskHook<T> {
-        handle: TaskHandle,
-        task_dump: Rc<RefCell<Option<T>>>,
-        value: Option<T>,
-    }
-
-    todo!()
-
-    // // whenever the task is complete, save it into th
-    // cx.use_hook(
-    //     move |_| {
-    //         let task_fut = task_initializer();
-
-    //         let task_dump = Rc::new(RefCell::new(None));
-
-    //         let slot = task_dump.clone();
-
-    //         let updater = cx.schedule_update_any();
-    //         let originator = cx.scope.our_arena_idx;
-
-    //         let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
-    //             *slot.as_ref().borrow_mut() = Some(output);
-    //             updater(originator);
-    //             originator
-    //         })));
-
-    //         TaskHook {
-    //             task_dump,
-    //             value: None,
-    //             handle,
-    //         }
-    //     },
-    //     |hook| {
-    //         if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
-    //             hook.value = Some(val);
-    //         }
-    //         (&hook.handle, &hook.value)
-    //     },
-    //     |_| {},
-    // )
-}
+// /// Awaits the given task, forcing the component to re-render when the value is ready.
+// ///
+// /// Returns the handle to the task and the value (if it is ready, else None).
+// ///
+// /// ```
+// /// static Example: FC<()> = |(cx, props)| {
+// ///     let (task, value) = use_task(|| async {
+// ///         timer::sleep(Duration::from_secs(1)).await;
+// ///         "Hello World"
+// ///     });
+// ///
+// ///     match contents {
+// ///         Some(contents) => rsx!(cx, div { "{title}" }),
+// ///         None => rsx!(cx, div { "Loading..." }),
+// ///     }
+// /// };
+// /// ```
+// pub fn use_coroutine<'src, Out, Fut, Init>(
+//     cx: Context<'src>,
+//     task_initializer: Init,
+// ) -> (&'src TaskHandle, &'src Option<Out>)
+// where
+//     Out: 'static,
+//     Fut: Future<Output = Out> + 'static,
+//     Init: FnOnce() -> Fut + 'src,
+// {
+//     struct TaskHook<T> {
+//         handle: TaskHandle,
+//         task_dump: Rc<RefCell<Option<T>>>,
+//         value: Option<T>,
+//     }
+
+//     todo!()
+
+//     // // whenever the task is complete, save it into th
+//     // cx.use_hook(
+//     //     move |_| {
+//     //         let task_fut = task_initializer();
+
+//     //         let task_dump = Rc::new(RefCell::new(None));
+
+//     //         let slot = task_dump.clone();
+
+//     //         let updater = cx.schedule_update_any();
+//     //         let originator = cx.scope.our_arena_idx;
+
+//     //         let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
+//     //             *slot.as_ref().borrow_mut() = Some(output);
+//     //             updater(originator);
+//     //             originator
+//     //         })));
+
+//     //         TaskHook {
+//     //             task_dump,
+//     //             value: None,
+//     //             handle,
+//     //         }
+//     //     },
+//     //     |hook| {
+//     //         if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
+//     //             hook.value = Some(val);
+//     //         }
+//     //         (&hook.handle, &hook.value)
+//     //     },
+//     //     |_| {},
+//     // )
+// }
 
 
 /// Asynchronously render new nodes once the given future has completed.
 /// Asynchronously render new nodes once the given future has completed.
 ///
 ///
@@ -95,11 +95,11 @@ pub fn use_suspense<'src, Out, Fut, Cb>(
     cx: Context<'src>,
     cx: Context<'src>,
     task_initializer: impl FnOnce() -> Fut,
     task_initializer: impl FnOnce() -> Fut,
     user_callback: Cb,
     user_callback: Cb,
-) -> Element<'src>
+) -> Element
 where
 where
     Fut: Future<Output = Out>,
     Fut: Future<Output = Out>,
     Out: 'static,
     Out: 'static,
-    Cb: FnMut(&Out) -> Element<'src> + 'src,
+    Cb: FnMut(&Out) -> Element + 'src,
 {
 {
     /*
     /*
     General strategy:
     General strategy:
@@ -167,10 +167,10 @@ where
     // )
     // )
 }
 }
 
 
-pub(crate) struct SuspenseHook {
-    pub handle: TaskHandle,
-    pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
-}
+// pub(crate) struct SuspenseHook {
+//     pub handle: TaskHandle,
+//     pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
+// }
 
 
 #[derive(Clone, Copy)]
 #[derive(Clone, Copy)]
 pub struct NodeRef<'src, T: 'static>(&'src RefCell<Option<T>>);
 pub struct NodeRef<'src, T: 'static>(&'src RefCell<Option<T>>);
@@ -183,5 +183,5 @@ impl<'a, T> Deref for NodeRef<'a, T> {
 }
 }
 
 
 pub fn use_node_ref<T, P>(cx: Context) -> NodeRef<T> {
 pub fn use_node_ref<T, P>(cx: Context) -> NodeRef<T> {
-    cx.use_hook(|_| RefCell::new(None), |f| NodeRef { 0: f }, |_| {})
+    cx.use_hook(|_| RefCell::new(None), |f| NodeRef { 0: f })
 }
 }

+ 0 - 0
packages/core/src/noderef.rs → packages/core/src/old/noderef.rs


+ 3 - 3
packages/core/src/resources.rs → packages/core/src/old/resources.rs

@@ -32,18 +32,18 @@ pub(crate) struct ResourcePool {
 
 
 impl ResourcePool {
 impl ResourcePool {
     /// this is unsafe because the caller needs to track which other scopes it's already using
     /// this is unsafe because the caller needs to track which other scopes it's already using
-    pub fn get_scope(&self, idx: ScopeId) -> Option<&ScopeInner> {
+    pub fn get_scope(&self, idx: &ScopeId) -> Option<&ScopeInner> {
         let inner = unsafe { &*self.components.get() };
         let inner = unsafe { &*self.components.get() };
         inner.get(idx.0)
         inner.get(idx.0)
     }
     }
 
 
     /// this is unsafe because the caller needs to track which other scopes it's already using
     /// this is unsafe because the caller needs to track which other scopes it's already using
-    pub fn get_scope_mut(&self, idx: ScopeId) -> Option<&mut ScopeInner> {
+    pub fn get_scope_mut(&self, idx: &ScopeId) -> Option<&mut ScopeInner> {
         let inner = unsafe { &mut *self.components.get() };
         let inner = unsafe { &mut *self.components.get() };
         inner.get_mut(idx.0)
         inner.get_mut(idx.0)
     }
     }
 
 
-    pub fn try_remove(&self, id: ScopeId) -> Option<ScopeInner> {
+    pub fn try_remove(&self, id: &ScopeId) -> Option<ScopeInner> {
         let inner = unsafe { &mut *self.components.get() };
         let inner = unsafe { &mut *self.components.get() };
         Some(inner.remove(id.0))
         Some(inner.remove(id.0))
         // .try_remove(id.0)
         // .try_remove(id.0)

+ 105 - 121
packages/core/src/scheduler.rs → packages/core/src/old/scheduler.rs

@@ -68,14 +68,17 @@ do anything too arduous from onInput.
 
 
 For the rest, we defer to the rIC period and work down each queue from high to low.
 For the rest, we defer to the rIC period and work down each queue from high to low.
 */
 */
-use crate::heuristics::*;
+
 use crate::innerlude::*;
 use crate::innerlude::*;
+use bumpalo::Bump;
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
 use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
 use fxhash::FxHashMap;
 use fxhash::FxHashMap;
 use fxhash::FxHashSet;
 use fxhash::FxHashSet;
 use indexmap::IndexSet;
 use indexmap::IndexSet;
 use slab::Slab;
 use slab::Slab;
+use std::pin::Pin;
+use std::task::Poll;
 use std::{
 use std::{
     any::{Any, TypeId},
     any::{Any, TypeId},
     cell::{Cell, UnsafeCell},
     cell::{Cell, UnsafeCell},
@@ -85,11 +88,7 @@ use std::{
 
 
 #[derive(Clone)]
 #[derive(Clone)]
 pub(crate) struct EventChannel {
 pub(crate) struct EventChannel {
-    pub task_counter: Rc<Cell<u64>>,
-    pub cur_subtree: Rc<Cell<u32>>,
     pub sender: UnboundedSender<SchedulerMsg>,
     pub sender: UnboundedSender<SchedulerMsg>,
-    pub schedule_any_immediate: Rc<dyn Fn(ScopeId)>,
-    pub submit_task: Rc<dyn Fn(FiberTask) -> TaskHandle>,
     pub get_shared_context: GetSharedContext,
     pub get_shared_context: GetSharedContext,
 }
 }
 
 
@@ -101,16 +100,6 @@ pub enum SchedulerMsg {
 
 
     // setstate
     // setstate
     Immediate(ScopeId),
     Immediate(ScopeId),
-
-    // tasks
-    Task(TaskMsg),
-}
-
-pub enum TaskMsg {
-    ToggleTask(u64),
-    PauseTask(u64),
-    ResumeTask(u64),
-    DropTask(u64),
 }
 }
 
 
 /// The scheduler holds basically everything around "working"
 /// The scheduler holds basically everything around "working"
@@ -126,16 +115,26 @@ pub enum TaskMsg {
 ///
 ///
 /// We can prevent this safety issue from occurring if we track which scopes are invalidated when starting a new task.
 /// We can prevent this safety issue from occurring if we track which scopes are invalidated when starting a new task.
 ///
 ///
+/// There's a lot of raw pointers here...
+///
+/// Since we're building self-referential structures for each component, we need to make sure that the referencs stay stable
+/// The best way to do that is a bump allocator.
+///
+///
 ///
 ///
 pub(crate) struct Scheduler {
 pub(crate) struct Scheduler {
-    /// All mounted components are arena allocated to make additions, removals, and references easy to work with
-    /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
-    ///
-    /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
-    /// and rusts's guarantees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
-    pub pool: ResourcePool,
+    // /// All mounted components are arena allocated to make additions, removals, and references easy to work with
+    // /// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
+    // ///
+    // /// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
+    // /// and rusts's guarantees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
+    // pub pool: ResourcePool,
+    //
+    pub component_arena: Bump,
+
+    pub free_components: VecDeque<*mut ScopeInner>,
 
 
-    pub heuristics: HeuristicsEngine,
+    pub heuristics: FxHashMap<FcSlot, Heuristic>,
 
 
     pub receiver: UnboundedReceiver<SchedulerMsg>,
     pub receiver: UnboundedReceiver<SchedulerMsg>,
 
 
@@ -145,104 +144,70 @@ pub(crate) struct Scheduler {
     // Every component that has futures that need to be polled
     // Every component that has futures that need to be polled
     pub pending_futures: FxHashSet<ScopeId>,
     pub pending_futures: FxHashSet<ScopeId>,
 
 
-    // In-flight futures
-    pub async_tasks: FuturesUnordered<FiberTask>,
-
     // // scheduler stuff
     // // scheduler stuff
     // pub current_priority: EventPriority,
     // pub current_priority: EventPriority,
     pub ui_events: VecDeque<UserEvent>,
     pub ui_events: VecDeque<UserEvent>,
 
 
     pub pending_immediates: VecDeque<ScopeId>,
     pub pending_immediates: VecDeque<ScopeId>,
 
 
-    pub pending_tasks: VecDeque<UserEvent>,
-
     pub batched_events: VecDeque<UserEvent>,
     pub batched_events: VecDeque<UserEvent>,
 
 
     pub garbage_scopes: HashSet<ScopeId>,
     pub garbage_scopes: HashSet<ScopeId>,
 
 
     pub dirty_scopes: IndexSet<ScopeId>,
     pub dirty_scopes: IndexSet<ScopeId>,
+
     pub saved_state: Option<SavedDiffWork<'static>>,
     pub saved_state: Option<SavedDiffWork<'static>>,
+
     pub in_progress: bool,
     pub in_progress: bool,
 }
 }
 
 
+pub type FcSlot = *const ();
+
+pub struct Heuristic {
+    hook_arena_size: usize,
+    node_arena_size: usize,
+}
+
 impl Scheduler {
 impl Scheduler {
     pub(crate) fn new(
     pub(crate) fn new(
         sender: UnboundedSender<SchedulerMsg>,
         sender: UnboundedSender<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
+        component_capacity: usize,
+        element_capacity: usize,
     ) -> Self {
     ) -> Self {
         /*
         /*
         Preallocate 2000 elements and 100 scopes to avoid dynamic allocation.
         Preallocate 2000 elements and 100 scopes to avoid dynamic allocation.
         Perhaps this should be configurable from some external config?
         Perhaps this should be configurable from some external config?
         */
         */
-        let components = Rc::new(UnsafeCell::new(Slab::with_capacity(100)));
-        let raw_elements = Rc::new(UnsafeCell::new(Slab::with_capacity(2000)));
 
 
-        let heuristics = HeuristicsEngine::new();
-
-        let task_counter = Rc::new(Cell::new(0));
-        let cur_subtree = Rc::new(Cell::new(0));
+        // let components = Rc::new(UnsafeCell::new(Slab::with_capacity(component_capacity)));
+        let raw_elements = Rc::new(UnsafeCell::new(Slab::with_capacity(element_capacity)));
 
 
         let channel = EventChannel {
         let channel = EventChannel {
-            cur_subtree,
-            task_counter: task_counter.clone(),
             sender: sender.clone(),
             sender: sender.clone(),
-            schedule_any_immediate: {
-                let sender = sender.clone();
-                Rc::new(move |id| {
-                    //
-                    log::debug!("scheduling immediate! {:?}", id);
-                    sender.unbounded_send(SchedulerMsg::Immediate(id)).unwrap()
-                })
-            },
-            // todo: we want to get the futures out of the scheduler message
-            // the scheduler message should be send/sync
-            submit_task: {
-                Rc::new(move |fiber_task| {
-                    let task_id = task_counter.get();
-                    task_counter.set(task_id + 1);
-
-                    todo!();
-                    // sender
-                    //     .unbounded_send(SchedulerMsg::Task(TaskMsg::SubmitTask(
-                    //         fiber_task, task_id,
-                    //     )))
-                    //     .unwrap();
-                    TaskHandle {
-                        our_id: task_id,
-                        sender: sender.clone(),
-                    }
-                })
-            },
             get_shared_context: {
             get_shared_context: {
-                let components = components.clone();
-                Rc::new(move |id, ty| {
-                    let components = unsafe { &*components.get() };
-                    let mut search: Option<&ScopeInner> = components.get(id.0);
-                    while let Some(inner) = search.take() {
-                        if let Some(shared) = inner.shared_contexts.borrow().get(&ty) {
-                            return Some(shared.clone());
-                        } else {
-                            search = inner.parent_idx.map(|id| components.get(id.0)).flatten();
-                        }
-                    }
-                    None
-                })
+                todo!()
+                // let components = components.clone();
+                // Rc::new(move |id, ty| {
+                //     let components = unsafe { &*components.get() };
+                //     let mut search: Option<&ScopeInner> = components.get(id.0);
+                //     while let Some(inner) = search.take() {
+                //         if let Some(shared) = inner.shared_contexts.borrow().get(&ty) {
+                //             return Some(shared.clone());
+                //         } else {
+                //             search = inner.parent_idx.map(|id| components.get(id.0)).flatten();
+                //         }
+                //     }
+                //     None
+                // })
             },
             },
         };
         };
 
 
-        let pool = ResourcePool {
-            components,
-            raw_elements,
-            channel,
-        };
-
-        let async_tasks = FuturesUnordered::new();
-
-        // push a task that would never resolve - prevents us from immediately aborting the scheduler
-        async_tasks.push(Box::pin(async {
-            std::future::pending::<()>().await;
-            ScopeId(0)
-        }) as FiberTask);
+        // let pool = ResourcePool {
+        //     components,
+        //     raw_elements,
+        //     channel,
+        // };
 
 
         let saved_state = SavedDiffWork {
         let saved_state = SavedDiffWork {
             mutations: Mutations::new(),
             mutations: Mutations::new(),
@@ -251,22 +216,15 @@ impl Scheduler {
         };
         };
 
 
         Self {
         Self {
-            pool,
-
+            // pool,
             receiver,
             receiver,
 
 
-            async_tasks,
-
             pending_garbage: FxHashSet::default(),
             pending_garbage: FxHashSet::default(),
 
 
-            heuristics,
-
             ui_events: VecDeque::new(),
             ui_events: VecDeque::new(),
 
 
             pending_immediates: VecDeque::new(),
             pending_immediates: VecDeque::new(),
 
 
-            pending_tasks: VecDeque::new(),
-
             batched_events: VecDeque::new(),
             batched_events: VecDeque::new(),
 
 
             garbage_scopes: HashSet::new(),
             garbage_scopes: HashSet::new(),
@@ -275,6 +233,8 @@ impl Scheduler {
             dirty_scopes: Default::default(),
             dirty_scopes: Default::default(),
             saved_state: Some(saved_state),
             saved_state: Some(saved_state),
             in_progress: false,
             in_progress: false,
+
+            heuristics: todo!(),
         }
         }
     }
     }
 
 
@@ -282,7 +242,7 @@ impl Scheduler {
     pub fn handle_ui_event(&mut self, event: UserEvent) -> bool {
     pub fn handle_ui_event(&mut self, event: UserEvent) -> bool {
         let (discrete, priority) = event_meta(&event);
         let (discrete, priority) = event_meta(&event);
 
 
-        if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+        if let Some(scope) = self.get_scope_mut(&event.scope) {
             if let Some(element) = event.mounted_dom_id {
             if let Some(element) = event.mounted_dom_id {
                 // TODO: bubble properly here
                 // TODO: bubble properly here
                 scope.call_listener(event, element);
                 scope.call_listener(event, element);
@@ -308,7 +268,7 @@ impl Scheduler {
 
 
     fn prepare_work(&mut self) {
     fn prepare_work(&mut self) {
         // while let Some(trigger) = self.ui_events.pop_back() {
         // while let Some(trigger) = self.ui_events.pop_back() {
-        //     if let Some(scope) = self.pool.get_scope_mut(trigger.scope) {}
+        //     if let Some(scope) = self.get_scope_mut(&trigger.scope) {}
         // }
         // }
     }
     }
 
 
@@ -328,15 +288,9 @@ impl Scheduler {
     unsafe fn load_work(&mut self) -> SavedDiffWork<'static> {
     unsafe fn load_work(&mut self) -> SavedDiffWork<'static> {
         self.saved_state.take().unwrap().extend()
         self.saved_state.take().unwrap().extend()
     }
     }
-    pub fn handle_task(&mut self, evt: TaskMsg) {
-        //
-    }
 
 
     pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
     pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
         match msg {
         match msg {
-            //
-            SchedulerMsg::Task(msg) => todo!(),
-
             SchedulerMsg::Immediate(_) => todo!(),
             SchedulerMsg::Immediate(_) => todo!(),
 
 
             SchedulerMsg::UiEvent(event) => {
             SchedulerMsg::UiEvent(event) => {
@@ -344,7 +298,7 @@ impl Scheduler {
 
 
                 let (discrete, priority) = event_meta(&event);
                 let (discrete, priority) = event_meta(&event);
 
 
-                if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+                if let Some(scope) = self.get_scope_mut(&event.scope) {
                     if let Some(element) = event.mounted_dom_id {
                     if let Some(element) = event.mounted_dom_id {
                         // TODO: bubble properly here
                         // TODO: bubble properly here
                         scope.call_listener(event, element);
                         scope.call_listener(event, element);
@@ -374,19 +328,19 @@ impl Scheduler {
         let saved_state = unsafe { self.load_work() };
         let saved_state = unsafe { self.load_work() };
 
 
         // We have to split away some parts of ourself - current lane is borrowed mutably
         // We have to split away some parts of ourself - current lane is borrowed mutably
-        let shared = self.pool.clone();
+        let shared = self.clone();
         let mut machine = unsafe { saved_state.promote(&shared) };
         let mut machine = unsafe { saved_state.promote(&shared) };
 
 
         let mut ran_scopes = FxHashSet::default();
         let mut ran_scopes = FxHashSet::default();
 
 
         if machine.stack.is_empty() {
         if machine.stack.is_empty() {
-            let shared = self.pool.clone();
+            let shared = self.clone();
 
 
             self.dirty_scopes
             self.dirty_scopes
-                .retain(|id| shared.get_scope(*id).is_some());
+                .retain(|id| shared.get_scope(id).is_some());
             self.dirty_scopes.sort_by(|a, b| {
             self.dirty_scopes.sort_by(|a, b| {
-                let h1 = shared.get_scope(*a).unwrap().height;
-                let h2 = shared.get_scope(*b).unwrap().height;
+                let h1 = shared.get_scope(a).unwrap().height;
+                let h2 = shared.get_scope(b).unwrap().height;
                 h1.cmp(&h2).reverse()
                 h1.cmp(&h2).reverse()
             });
             });
 
 
@@ -396,8 +350,8 @@ impl Scheduler {
                     ran_scopes.insert(scopeid);
                     ran_scopes.insert(scopeid);
                     log::debug!("about to run scope {:?}", scopeid);
                     log::debug!("about to run scope {:?}", scopeid);
 
 
-                    if let Some(component) = self.pool.get_scope_mut(scopeid) {
-                        if component.run_scope(&self.pool) {
+                    if let Some(component) = self.get_scope_mut(&scopeid) {
+                        if component.run_scope(&self) {
                             let (old, new) =
                             let (old, new) =
                                 (component.frames.wip_head(), component.frames.fin_head());
                                 (component.frames.wip_head(), component.frames.fin_head());
                             // let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
                             // let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
@@ -494,7 +448,6 @@ impl Scheduler {
         while self.has_any_work() {
         while self.has_any_work() {
             while let Ok(Some(msg)) = self.receiver.try_next() {
             while let Ok(Some(msg)) = self.receiver.try_next() {
                 match msg {
                 match msg {
-                    SchedulerMsg::Task(t) => todo!(),
                     SchedulerMsg::Immediate(im) => {
                     SchedulerMsg::Immediate(im) => {
                         self.dirty_scopes.insert(im);
                         self.dirty_scopes.insert(im);
                     }
                     }
@@ -506,7 +459,7 @@ impl Scheduler {
 
 
             // switch our priority, pop off any work
             // switch our priority, pop off any work
             while let Some(event) = self.ui_events.pop_front() {
             while let Some(event) = self.ui_events.pop_front() {
-                if let Some(scope) = self.pool.get_scope_mut(event.scope) {
+                if let Some(scope) = self.get_scope_mut(&event.scope) {
                     if let Some(element) = event.mounted_dom_id {
                     if let Some(element) = event.mounted_dom_id {
                         log::info!("Calling listener {:?}, {:?}", event.scope, element);
                         log::info!("Calling listener {:?}, {:?}", event.scope, element);
 
 
@@ -519,7 +472,6 @@ impl Scheduler {
                                     self.dirty_scopes.insert(im);
                                     self.dirty_scopes.insert(im);
                                 }
                                 }
                                 SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
                                 SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
-                                SchedulerMsg::Task(_) => todo!(),
                             }
                             }
                         }
                         }
                     }
                     }
@@ -562,19 +514,19 @@ impl Scheduler {
     ///
     ///
     /// Typically used to kickstart the VirtualDOM after initialization.
     /// Typically used to kickstart the VirtualDOM after initialization.
     pub fn rebuild(&mut self, base_scope: ScopeId) -> Mutations {
     pub fn rebuild(&mut self, base_scope: ScopeId) -> Mutations {
-        let mut shared = self.pool.clone();
+        let mut shared = self.clone();
         let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared);
         let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared);
 
 
         // TODO: drain any in-flight work
         // TODO: drain any in-flight work
         let cur_component = self
         let cur_component = self
             .pool
             .pool
-            .get_scope_mut(base_scope)
+            .get_scope_mut(&base_scope)
             .expect("The base scope should never be moved");
             .expect("The base scope should never be moved");
 
 
         log::debug!("rebuild {:?}", base_scope);
         log::debug!("rebuild {:?}", base_scope);
 
 
         // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
         // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
-        if cur_component.run_scope(&self.pool) {
+        if cur_component.run_scope(&self) {
             diff_machine
             diff_machine
                 .stack
                 .stack
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
@@ -596,13 +548,13 @@ impl Scheduler {
     pub fn hard_diff(&mut self, base_scope: ScopeId) -> Mutations {
     pub fn hard_diff(&mut self, base_scope: ScopeId) -> Mutations {
         let cur_component = self
         let cur_component = self
             .pool
             .pool
-            .get_scope_mut(base_scope)
+            .get_scope_mut(&base_scope)
             .expect("The base scope should never be moved");
             .expect("The base scope should never be moved");
 
 
         log::debug!("hard diff {:?}", base_scope);
         log::debug!("hard diff {:?}", base_scope);
 
 
-        if cur_component.run_scope(&self.pool) {
-            let mut diff_machine = DiffMachine::new(Mutations::new(), &mut self.pool);
+        if cur_component.run_scope(&self) {
+            let mut diff_machine = DiffMachine::new(Mutations::new(), &mut self);
             diff_machine.cfg.force_diff = true;
             diff_machine.cfg.force_diff = true;
             diff_machine.diff_scope(base_scope);
             diff_machine.diff_scope(base_scope);
             diff_machine.mutations
             diff_machine.mutations
@@ -611,3 +563,35 @@ impl Scheduler {
         }
         }
     }
     }
 }
 }
+
+impl Future for Scheduler {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
+        let mut all_pending = true;
+
+        for fut in self.pending_futures.iter() {
+            let scope = self
+                .pool
+                .get_scope_mut(&fut)
+                .expect("Scope should never be moved");
+
+            let items = scope.items.get_mut();
+            for task in items.tasks.iter_mut() {
+                let t = task.as_mut();
+                let g = unsafe { Pin::new_unchecked(t) };
+                match g.poll(cx) {
+                    Poll::Ready(r) => {
+                        all_pending = false;
+                    }
+                    Poll::Pending => {}
+                }
+            }
+        }
+
+        match all_pending {
+            true => Poll::Pending,
+            false => Poll::Ready(()),
+        }
+    }
+}

+ 0 - 0
packages/core/src/threadsafe.rs → packages/core/src/old/threadsafe.rs


+ 220 - 329
packages/core/src/scope.rs

@@ -1,44 +1,36 @@
 use crate::innerlude::*;
 use crate::innerlude::*;
 
 
+use futures_channel::mpsc::UnboundedSender;
 use fxhash::FxHashMap;
 use fxhash::FxHashMap;
 use std::{
 use std::{
     any::{Any, TypeId},
     any::{Any, TypeId},
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
     collections::HashMap,
     collections::HashMap,
     future::Future,
     future::Future,
-    pin::Pin,
     rc::Rc,
     rc::Rc,
 };
 };
 
 
-use crate::{innerlude::*, lazynodes::LazyNodes};
 use bumpalo::{boxed::Box as BumpBox, Bump};
 use bumpalo::{boxed::Box as BumpBox, Bump};
-use std::ops::Deref;
 
 
 /// Components in Dioxus use the "Context" object to interact with their lifecycle.
 /// Components in Dioxus use the "Context" object to interact with their lifecycle.
 ///
 ///
 /// This lets components access props, schedule updates, integrate hooks, and expose shared state.
 /// This lets components access props, schedule updates, integrate hooks, and expose shared state.
 ///
 ///
-/// Note: all of these methods are *imperative* - they do not act as hooks! They are meant to be used by hooks
-/// to provide complex behavior. For instance, calling "add_shared_state" on every render is considered a leak. This method
-/// exists for the `use_provide_state` hook to provide a shared state object.
-///
 /// For the most part, the only method you should be using regularly is `render`.
 /// For the most part, the only method you should be using regularly is `render`.
 ///
 ///
 /// ## Example
 /// ## Example
 ///
 ///
 /// ```ignore
 /// ```ignore
-/// #[derive(Properties)]
-/// struct Props {
+/// #[derive(Props)]
+/// struct ExampleProps {
 ///     name: String
 ///     name: String
 /// }
 /// }
 ///
 ///
-/// fn example(cx: Context<Props>) -> VNode {
-///     html! {
-///         <div> "Hello, {cx.name}" </div>
-///     }
+/// fn Example((cx, props): Scope<Props>) -> Element {
+///     cx.render(rsx!{ div {"Hello, {props.name}"} })
 /// }
 /// }
 /// ```
 /// ```
-pub type Context<'a> = &'a ScopeInner;
+pub type Context<'a> = &'a Scope;
 
 
 /// Every component in Dioxus is represented by a `Scope`.
 /// Every component in Dioxus is represented by a `Scope`.
 ///
 ///
@@ -49,17 +41,33 @@ pub type Context<'a> = &'a ScopeInner;
 ///
 ///
 /// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
 /// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
 /// use case they might have.
 /// use case they might have.
-pub struct ScopeInner {
+pub struct Scope {
     // Book-keeping about our spot in the arena
     // Book-keeping about our spot in the arena
-    pub(crate) parent_idx: Option<ScopeId>,
+
+    // safety:
+    //
+    // pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
+    // this is just a bit of a hack to not need an Rc to the ScopeArena.
+    // todo: replace this will ScopeId and provide a connection to scope arena directly
+    pub(crate) parent_scope: Option<*mut Scope>,
+
     pub(crate) our_arena_idx: ScopeId,
     pub(crate) our_arena_idx: ScopeId,
+
     pub(crate) height: u32,
     pub(crate) height: u32,
+
     pub(crate) subtree: Cell<u32>,
     pub(crate) subtree: Cell<u32>,
+
     pub(crate) is_subtree_root: Cell<bool>,
     pub(crate) is_subtree_root: Cell<bool>,
 
 
-    // Nodes
-    pub(crate) frames: ActiveFrame,
-    pub(crate) caller: BumpBox<'static, dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>>,
+    pub(crate) generation: Cell<u32>,
+
+    // The double-buffering situation that we will use
+    pub(crate) frames: [BumpFrame; 2],
+
+    pub(crate) old_root: RefCell<Option<NodeLink>>,
+    pub(crate) new_root: RefCell<Option<NodeLink>>,
+
+    pub(crate) caller: *mut dyn Fn(&Scope) -> Element,
 
 
     /*
     /*
     we care about:
     we care about:
@@ -67,12 +75,7 @@ pub struct ScopeInner {
     - borrowed props (and how to drop them when the parent is dropped)
     - borrowed props (and how to drop them when the parent is dropped)
     - suspended nodes (and how to call their callback when their associated tasks are complete)
     - suspended nodes (and how to call their callback when their associated tasks are complete)
     */
     */
-    pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
-    pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
-    pub(crate) suspended_nodes: RefCell<FxHashMap<u64, *const VSuspended<'static>>>,
-
-    pub(crate) tasks: RefCell<Vec<BumpBox<'static, dyn Future<Output = ()>>>>,
-    pub(crate) pending_effects: RefCell<Vec<BumpBox<'static, dyn FnMut()>>>,
+    pub(crate) items: RefCell<SelfReferentialItems<'static>>,
 
 
     // State
     // State
     pub(crate) hooks: HookList,
     pub(crate) hooks: HookList,
@@ -80,34 +83,19 @@ pub struct ScopeInner {
     // todo: move this into a centralized place - is more memory efficient
     // todo: move this into a centralized place - is more memory efficient
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
     pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
 
 
-    // whenever set_state is called, we fire off a message to the scheduler
-    // this closure _is_ the method called by schedule_update that marks this component as dirty
-    pub(crate) memoized_updater: Rc<dyn Fn()>,
-
-    pub(crate) shared: EventChannel,
+    pub(crate) sender: UnboundedSender<SchedulerMsg>,
 }
 }
 
 
-/// Public interface for Scopes.
-impl ScopeInner {
-    /// Get the root VNode for this Scope.
-    ///
-    /// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
-    ///
-    /// # Example
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// if let VNode::VElement(node) = base.root_node() {
-    ///     assert_eq!(node.tag_name, "div");
-    /// }
-    /// ```
-    pub fn root_node(&self) -> &VNode {
-        self.frames.fin_head()
-    }
+pub struct SelfReferentialItems<'a> {
+    pub(crate) listeners: Vec<&'a Listener<'a>>,
+    pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
+    pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended<'a>>,
+    pub(crate) tasks: Vec<BumpBox<'a, dyn Future<Output = ()>>>,
+    pub(crate) pending_effects: Vec<BumpBox<'a, dyn FnMut()>>,
+}
 
 
+// Public methods exposed to libraries and components
+impl Scope {
     /// Get the subtree ID that this scope belongs to.
     /// Get the subtree ID that this scope belongs to.
     ///
     ///
     /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
     /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
@@ -128,16 +116,6 @@ impl ScopeInner {
         self.subtree.get()
         self.subtree.get()
     }
     }
 
 
-    pub(crate) fn new_subtree(&self) -> Option<u32> {
-        if self.is_subtree_root.get() {
-            None
-        } else {
-            let cur = self.shared.cur_subtree.get();
-            self.shared.cur_subtree.set(cur + 1);
-            Some(cur)
-        }
-    }
-
     /// Get the height of this Scope - IE the number of scopes above it.
     /// Get the height of this Scope - IE the number of scopes above it.
     ///
     ///
     /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
     /// A Scope with a height of `0` is the root scope - there are no other scopes above it.
@@ -173,7 +151,10 @@ impl ScopeInner {
     /// assert_eq!(base.parent(), None);
     /// assert_eq!(base.parent(), None);
     /// ```
     /// ```
     pub fn parent(&self) -> Option<ScopeId> {
     pub fn parent(&self) -> Option<ScopeId> {
-        self.parent_idx
+        match self.parent_scope {
+            Some(p) => Some(unsafe { &*p }.our_arena_idx),
+            None => None,
+        }
     }
     }
 
 
     /// Get the ID of this Scope within this Dioxus VirtualDOM.
     /// Get the ID of this Scope within this Dioxus VirtualDOM.
@@ -192,250 +173,50 @@ impl ScopeInner {
     pub fn scope_id(&self) -> ScopeId {
     pub fn scope_id(&self) -> ScopeId {
         self.our_arena_idx
         self.our_arena_idx
     }
     }
-}
 
 
-// The type of closure that wraps calling components
-/// The type of task that gets sent to the task scheduler
-/// Submitting a fiber task returns a handle to that task, which can be used to wake up suspended nodes
-pub type FiberTask = Pin<Box<dyn Future<Output = ScopeId>>>;
-
-/// Private interface for Scopes.
-impl ScopeInner {
-    // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
-    // we are going to break this lifetime by force in order to save it on ourselves.
-    // To make sure that the lifetime isn't truly broken, we receive a Weak RC so we can't keep it around after the parent dies.
-    // This should never happen, but is a good check to keep around
-    //
-    // Scopes cannot be made anywhere else except for this file
-    // Therefore, their lifetimes are connected exclusively to the virtual dom
-    pub(crate) fn new(
-        caller: BumpBox<dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>>,
-        our_arena_idx: ScopeId,
-        parent_idx: Option<ScopeId>,
-        height: u32,
-        subtree: u32,
-        shared: EventChannel,
-    ) -> Self {
-        let schedule_any_update = shared.schedule_any_immediate.clone();
-
-        let memoized_updater = Rc::new(move || schedule_any_update(our_arena_idx));
-
-        // wipe away the associated lifetime - we are going to manually manage the one-way lifetime graph
-        let caller = unsafe { std::mem::transmute(caller) };
-
-        Self {
-            memoized_updater,
-            shared,
-            caller,
-            parent_idx,
-            our_arena_idx,
-            height,
-            subtree: Cell::new(subtree),
-            is_subtree_root: Cell::new(false),
-            tasks: Default::default(),
-            frames: ActiveFrame::new(),
-            hooks: Default::default(),
-
-            pending_effects: Default::default(),
-            suspended_nodes: Default::default(),
-            shared_contexts: Default::default(),
-            listeners: Default::default(),
-            borrowed_props: Default::default(),
-        }
-    }
-
-    pub(crate) fn update_scope_dependencies(
-        &mut self,
-        caller: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
-    ) {
-        log::debug!("Updating scope dependencies {:?}", self.our_arena_idx);
-        let caller = caller as *const _;
-        self.caller = unsafe { std::mem::transmute(caller) };
-    }
-
-    /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
-    /// causing UB in our tree.
-    ///
-    /// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
-    /// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
-    /// any possible references to the data in the hook list.
-    ///
-    /// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
-    /// all listeners and borrowed props so we can clear them here.
-    ///
-    /// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
-    /// be dropped.
-    pub(crate) fn ensure_drop_safety(&mut self, pool: &ResourcePool) {
-        // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
-        // run the hooks (which hold an &mut Reference)
-        // right now, we don't drop
-        self.borrowed_props
-            .get_mut()
-            .drain(..)
-            .map(|li| unsafe { &*li })
-            .for_each(|comp| {
-                // First drop the component's undropped references
-                let scope_id = comp
-                    .associated_scope
-                    .get()
-                    .expect("VComponents should be associated with a valid Scope");
-
-                if let Some(scope) = pool.get_scope_mut(scope_id) {
-                    scope.ensure_drop_safety(pool);
-
-                    let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
-                    drop_props();
-                }
-            });
-
-        // Now that all the references are gone, we can safely drop our own references in our listeners.
-        self.listeners
-            .get_mut()
-            .drain(..)
-            .map(|li| unsafe { &*li })
-            .for_each(|listener| drop(listener.callback.borrow_mut().take()));
-    }
-
-    /// A safe wrapper around calling listeners
-    pub(crate) fn call_listener(&mut self, event: UserEvent, element: ElementId) {
-        let listners = self.listeners.borrow_mut();
-
-        let raw_listener = listners.iter().find(|lis| {
-            let search = unsafe { &***lis };
-            if search.event == event.name {
-                let search_id = search.mounted_node.get();
-                search_id.map(|f| f == element).unwrap_or(false)
-            } else {
-                false
-            }
-        });
-
-        if let Some(raw_listener) = raw_listener {
-            let listener = unsafe { &**raw_listener };
-            let mut cb = listener.callback.borrow_mut();
-            if let Some(cb) = cb.as_mut() {
-                (cb)(event.event);
-            }
-        } else {
-            log::warn!("An event was triggered but there was no listener to handle it");
-        }
-    }
-
-    /*
-    General strategy here is to load up the appropriate suspended task and then run it.
-    Suspended nodes cannot be called repeatedly.
-    */
-    pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
-        let mut nodes = self.suspended_nodes.borrow_mut();
-
-        if let Some(suspended) = nodes.remove(&task_id) {
-            let sus: &'a VSuspended<'static> = unsafe { &*suspended };
-            let sus: &'a VSuspended<'a> = unsafe { std::mem::transmute(sus) };
-            let mut boxed = sus.callback.borrow_mut().take().unwrap();
-            let new_node: Element<'a> = boxed();
-        }
-    }
-
-    // run the list of effects
-    pub(crate) fn run_effects(&mut self, pool: &ResourcePool) {
-        todo!()
-        // let mut effects = self.frames.effects.borrow_mut();
-        // let mut effects = effects.drain(..).collect::<Vec<_>>();
-
-        // for effect in effects {
-        //     let effect = unsafe { &*effect };
-        //     let effect = effect.as_ref();
-
-        //     let mut effect = effect.borrow_mut();
-        //     let mut effect = effect.as_mut();
-
-        //     effect.run(pool);
-        // }
-    }
-
-    /// Render this component.
-    ///
-    /// Returns true if the scope completed successfully and false if running failed (IE a None error was propagated).
-    pub(crate) fn run_scope<'sel>(&'sel mut self, pool: &ResourcePool) -> bool {
-        // Cycle to the next frame and then reset it
-        // This breaks any latent references, invalidating every pointer referencing into it.
-        // Remove all the outdated listeners
-        self.ensure_drop_safety(pool);
-
-        // Safety:
-        // - We dropped the listeners, so no more &mut T can be used while these are held
-        // - All children nodes that rely on &mut T are replaced with a new reference
-        unsafe { self.hooks.reset() };
-
-        // Safety:
-        // - We've dropped all references to the wip bump frame
-        unsafe { self.frames.reset_wip_frame() };
-
-        // just forget about our suspended nodes while we're at it
-        self.suspended_nodes.get_mut().clear();
-
-        // guarantee that we haven't screwed up - there should be no latent references anywhere
-        debug_assert!(self.listeners.borrow().is_empty());
-        debug_assert!(self.suspended_nodes.borrow().is_empty());
-        debug_assert!(self.borrowed_props.borrow().is_empty());
-
-        log::debug!("Borrowed stuff is successfully cleared");
-
-        // Cast the caller ptr from static to one with our own reference
-        let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = unsafe { &*self.caller };
-
-        // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
-        if let Some(builder) = render(self) {
-            let new_head = builder.into_vnode(NodeFactory {
-                bump: &self.frames.wip_frame().bump,
-            });
-            log::debug!("Render is successful");
-
-            // the user's component succeeded. We can safely cycle to the next frame
-            self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
-            self.frames.cycle_frame();
-
-            true
-        } else {
-            false
-        }
-    }
     /// Create a subscription that schedules a future render for the reference component
     /// Create a subscription that schedules a future render for the reference component
     ///
     ///
     /// ## Notice: you should prefer using prepare_update and get_scope_id
     /// ## Notice: you should prefer using prepare_update and get_scope_id
     pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
     pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
-        self.memoized_updater.clone()
+        // pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
+        let chan = self.sender.clone();
+        let id = self.scope_id();
+        Rc::new(move || {
+            let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
+        })
+    }
+
+    /// Schedule an update for any component given its ScopeId.
+    ///
+    /// A component's ScopeId can be obtained from `use_hook` or the [`Context::scope_id`] method.
+    ///
+    /// This method should be used when you want to schedule an update for a component
+    pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
+        let chan = self.sender.clone();
+        Rc::new(move |id| {
+            let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
+        })
     }
     }
 
 
     /// Get the [`ScopeId`] of a mounted component.
     /// Get the [`ScopeId`] of a mounted component.
     ///
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn needs_update(&self) {
     pub fn needs_update(&self) {
-        (self.memoized_updater)()
+        self.needs_update_any(self.scope_id())
     }
     }
 
 
     /// Get the [`ScopeId`] of a mounted component.
     /// Get the [`ScopeId`] of a mounted component.
     ///
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn needs_update_any(&self, id: ScopeId) {
     pub fn needs_update_any(&self, id: ScopeId) {
-        (self.shared.schedule_any_immediate)(id)
-    }
-
-    /// Schedule an update for any component given its ScopeId.
-    ///
-    /// A component's ScopeId can be obtained from `use_hook` or the [`Context::scope_id`] method.
-    ///
-    /// This method should be used when you want to schedule an update for a component
-    pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
-        self.shared.schedule_any_immediate.clone()
+        let _ = self.sender.unbounded_send(SchedulerMsg::Immediate(id));
     }
     }
 
 
     /// Get the [`ScopeId`] of a mounted component.
     /// Get the [`ScopeId`] of a mounted component.
     ///
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn bump(&self) -> &Bump {
     pub fn bump(&self) -> &Bump {
-        let bump = &self.frames.wip_frame().bump;
-        bump
+        &self.wip_frame().bump
     }
     }
 
 
     /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
     /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
@@ -445,21 +226,28 @@ impl ScopeInner {
     /// ## Example
     /// ## Example
     ///
     ///
     /// ```ignore
     /// ```ignore
-    /// fn Component(cx: Context<()>) -> VNode {
+    /// fn Component(cx: Scope, props: &Props) -> Element {
     ///     // Lazy assemble the VNode tree
     ///     // Lazy assemble the VNode tree
-    ///     let lazy_tree = html! {<div> "Hello World" </div>};
+    ///     let lazy_nodes = rsx!("hello world");
     ///
     ///
     ///     // Actually build the tree and allocate it
     ///     // Actually build the tree and allocate it
     ///     cx.render(lazy_tree)
     ///     cx.render(lazy_tree)
     /// }
     /// }
     ///```
     ///```
-    pub fn render<'src>(
-        &'src self,
-        lazy_nodes: Option<LazyNodes<'src, '_>>,
-    ) -> Option<VNode<'src>> {
-        let bump = &self.frames.wip_frame().bump;
+    pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
+        let bump = &self.wip_frame().bump;
         let factory = NodeFactory { bump };
         let factory = NodeFactory { bump };
-        lazy_nodes.map(|f| f.call(factory))
+        let node = lazy_nodes.map(|f| f.call(factory))?;
+
+        let idx = self
+            .wip_frame()
+            .add_node(unsafe { std::mem::transmute(node) });
+
+        Some(NodeLink {
+            gen_id: self.generation.get(),
+            scope_id: self.our_arena_idx,
+            link_idx: idx,
+        })
     }
     }
 
 
     /// Push an effect to be ran after the component has been successfully mounted to the dom
     /// Push an effect to be ran after the component has been successfully mounted to the dom
@@ -475,8 +263,9 @@ impl ScopeInner {
         // erase the 'src lifetime for self-referential storage
         // erase the 'src lifetime for self-referential storage
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
 
 
-        self.pending_effects.borrow_mut().push(self_ref_fut);
-        self.pending_effects.borrow().len() - 1
+        let mut items = self.items.borrow_mut();
+        items.pending_effects.push(self_ref_fut);
+        items.pending_effects.len() - 1
     }
     }
 
 
     /// Pushes the future onto the poll queue to be polled
     /// Pushes the future onto the poll queue to be polled
@@ -491,8 +280,9 @@ impl ScopeInner {
         // erase the 'src lifetime for self-referential storage
         // erase the 'src lifetime for self-referential storage
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
         let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
 
 
-        self.tasks.borrow_mut().push(self_ref_fut);
-        self.tasks.borrow().len() - 1
+        let mut items = self.items.borrow_mut();
+        items.tasks.push(self_ref_fut);
+        items.tasks.len() - 1
     }
     }
 
 
     /// This method enables the ability to expose state to children further down the VirtualDOM Tree.
     /// This method enables the ability to expose state to children further down the VirtualDOM Tree.
@@ -519,7 +309,7 @@ impl ScopeInner {
     ///     rsx!(cx, div { "hello {state.0}" })
     ///     rsx!(cx, div { "hello {state.0}" })
     /// }
     /// }
     /// ```
     /// ```
-    pub fn provide_state<T>(self, value: T)
+    pub fn provide_state<T>(&self, value: T)
     where
     where
         T: 'static,
         T: 'static,
     {
     {
@@ -531,11 +321,21 @@ impl ScopeInner {
     }
     }
 
 
     /// Try to retrieve a SharedState with type T from the any parent Scope.
     /// Try to retrieve a SharedState with type T from the any parent Scope.
-    pub fn consume_state<T: 'static>(self) -> Option<Rc<T>> {
-        let getter = &self.shared.get_shared_context;
-        let ty = TypeId::of::<T>();
-        let idx = self.our_arena_idx;
-        getter(idx, ty).map(|f| f.downcast().unwrap())
+    pub fn consume_state<T: 'static>(&self) -> Option<Rc<T>> {
+        if let Some(shared) = self.shared_contexts.borrow().get(&TypeId::of::<T>()) {
+            Some(shared.clone().downcast::<T>().unwrap())
+        } else {
+            let mut search_parent = self.parent_scope;
+
+            while let Some(parent_ptr) = search_parent {
+                let parent = unsafe { &*parent_ptr };
+                if let Some(shared) = parent.shared_contexts.borrow().get(&TypeId::of::<T>()) {
+                    return Some(shared.clone().downcast::<T>().unwrap());
+                }
+                search_parent = parent.parent_scope;
+            }
+            None
+        }
     }
     }
 
 
     /// Create a new subtree with this scope as the root of the subtree.
     /// Create a new subtree with this scope as the root of the subtree.
@@ -553,7 +353,7 @@ impl ScopeInner {
     ///     rsx!(cx, div { "Subtree {id}"})
     ///     rsx!(cx, div { "Subtree {id}"})
     /// };
     /// };
     /// ```
     /// ```
-    pub fn create_subtree(self) -> Option<u32> {
+    pub fn create_subtree(&self) -> Option<u32> {
         self.new_subtree()
         self.new_subtree()
     }
     }
 
 
@@ -570,7 +370,7 @@ impl ScopeInner {
     ///     rsx!(cx, div { "Subtree {id}"})
     ///     rsx!(cx, div { "Subtree {id}"})
     /// };
     /// };
     /// ```
     /// ```
-    pub fn get_current_subtree(self) -> u32 {
+    pub fn get_current_subtree(&self) -> u32 {
         self.subtree()
         self.subtree()
     }
     }
 
 
@@ -580,8 +380,9 @@ impl ScopeInner {
     ///
     ///
     /// - Initializer: closure used to create the initial hook state
     /// - Initializer: closure used to create the initial hook state
     /// - Runner: closure used to output a value every time the hook is used
     /// - Runner: closure used to output a value every time the hook is used
-    /// - Cleanup: closure used to teardown the hook once the dom is cleaned up
     ///
     ///
+    /// To "cleanup" the hook, implement `Drop` on the stored hook value. Whenever the component is dropped, the hook
+    /// will be dropped as well.
     ///
     ///
     /// # Example
     /// # Example
     ///
     ///
@@ -591,42 +392,132 @@ impl ScopeInner {
     ///     use_hook(
     ///     use_hook(
     ///         || Rc::new(RefCell::new(initial_value())),
     ///         || Rc::new(RefCell::new(initial_value())),
     ///         |state| state,
     ///         |state| state,
-    ///         |_| {},
     ///     )
     ///     )
     /// }
     /// }
     /// ```
     /// ```
-    pub fn use_hook<'src, State, Output, Init, Run, Cleanup>(
+    pub fn use_hook<'src, State: 'static, Output: 'src>(
         &'src self,
         &'src self,
-        initializer: Init,
-        runner: Run,
-        cleanup: Cleanup,
-    ) -> Output
-    where
-        State: 'static,
-        Output: 'src,
-        Init: FnOnce(usize) -> State,
-        Run: FnOnce(&'src mut State) -> Output,
-        Cleanup: FnOnce(Box<State>) + 'static,
-    {
-        // If the idx is the same as the hook length, then we need to add the current hook
+        initializer: impl FnOnce(usize) -> State,
+        runner: impl FnOnce(&'src mut State) -> Output,
+    ) -> Output {
         if self.hooks.at_end() {
         if self.hooks.at_end() {
-            self.hooks.push_hook(
-                initializer(self.hooks.len()),
-                Box::new(|raw| {
-                    let s = raw.downcast::<State>().unwrap();
-                    cleanup(s);
-                }),
-            );
+            self.hooks.push_hook(initializer(self.hooks.len()));
         }
         }
 
 
-        runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
-    }
-}
-
-const HOOK_ERR_MSG: &str = r###"
+        const HOOK_ERR_MSG: &str = r###"
 Unable to retrieve the hook that was initialized at this index.
 Unable to retrieve the hook that was initialized at this index.
 Consult the `rules of hooks` to understand how to use hooks properly.
 Consult the `rules of hooks` to understand how to use hooks properly.
 
 
 You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
 You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
 Functions prefixed with "use" should never be called conditionally.
 Functions prefixed with "use" should never be called conditionally.
 "###;
 "###;
+
+        runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
+    }
+}
+
+// Important internal methods
+impl Scope {
+    /// The "work in progress frame" represents the frame that is currently being worked on.
+    pub(crate) fn wip_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 0 {
+            true => &self.frames[0],
+            false => &self.frames[1],
+        }
+    }
+    pub(crate) fn fin_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 1 {
+            true => &self.frames[0],
+            false => &self.frames[1],
+        }
+    }
+
+    pub unsafe fn reset_wip_frame(&self) {
+        // todo: unsafecell or something
+        let bump = self.wip_frame() as *const _ as *mut Bump;
+        let g = &mut *bump;
+        g.reset();
+    }
+
+    pub fn cycle_frame(&self) {
+        self.generation.set(self.generation.get() + 1);
+    }
+
+    /// A safe wrapper around calling listeners
+    pub(crate) fn call_listener(&self, event: UserEvent, element: ElementId) {
+        let listners = &mut self.items.borrow_mut().listeners;
+
+        let listener = listners.iter().find(|lis| {
+            let search = lis;
+            if search.event == event.name {
+                let search_id = search.mounted_node.get();
+                search_id.map(|f| f == element).unwrap_or(false)
+            } else {
+                false
+            }
+        });
+
+        if let Some(listener) = listener {
+            let mut cb = listener.callback.borrow_mut();
+            if let Some(cb) = cb.as_mut() {
+                (cb)(event.event);
+            }
+        } else {
+            log::warn!("An event was triggered but there was no listener to handle it");
+        }
+    }
+
+    // General strategy here is to load up the appropriate suspended task and then run it.
+    // Suspended nodes cannot be called repeatedly.
+    pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
+        let mut nodes = &mut self.items.get_mut().suspended_nodes;
+
+        if let Some(suspended) = nodes.remove(&task_id) {
+            let sus: &'a VSuspended<'static> = unsafe { &*suspended };
+            let sus: &'a VSuspended<'a> = unsafe { std::mem::transmute(sus) };
+            let mut boxed = sus.callback.borrow_mut().take().unwrap();
+            let new_node: Element = boxed();
+        }
+    }
+
+    // run the list of effects
+    pub(crate) fn run_effects(&mut self) {
+        for mut effect in self.items.get_mut().pending_effects.drain(..) {
+            effect();
+        }
+    }
+
+    pub(crate) fn new_subtree(&self) -> Option<u32> {
+        todo!()
+        // if self.is_subtree_root.get() {
+        //     None
+        // } else {
+        //     let cur = self.shared.cur_subtree.get();
+        //     self.shared.cur_subtree.set(cur + 1);
+        //     Some(cur)
+        // }
+    }
+}
+
+pub struct BumpFrame {
+    pub bump: Bump,
+    pub nodes: RefCell<Vec<VNode<'static>>>,
+}
+impl BumpFrame {
+    pub fn new() -> Self {
+        let bump = Bump::new();
+
+        let node = &*bump.alloc(VText {
+            text: "asd",
+            dom_id: Default::default(),
+            is_static: false,
+        });
+        let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]);
+        Self { bump, nodes }
+    }
+    fn add_node(&self, node: VNode<'static>) -> usize {
+        let mut nodes = self.nodes.borrow_mut();
+        nodes.push(node);
+        nodes.len() - 1
+    }
+}

+ 290 - 0
packages/core/src/scopearena.rs

@@ -0,0 +1,290 @@
+use slab::Slab;
+use std::{
+    borrow::BorrowMut,
+    cell::{Cell, RefCell},
+};
+
+use bumpalo::{boxed::Box as BumpBox, Bump};
+use futures_channel::mpsc::UnboundedSender;
+
+use crate::innerlude::*;
+
+pub type FcSlot = *const ();
+// pub heuristics: FxHashMap<FcSlot, Heuristic>,
+
+pub struct Heuristic {
+    hook_arena_size: usize,
+    node_arena_size: usize,
+}
+
+// a slab-like arena with stable references even when new scopes are allocated
+// uses a bump arena as a backing
+//
+// has an internal heuristics engine to pre-allocate arenas to the right size
+pub(crate) struct ScopeArena {
+    bump: Bump,
+    scopes: Vec<*mut Scope>,
+    free_scopes: Vec<ScopeId>,
+    nodes: RefCell<Slab<*const VNode<'static>>>,
+    pub(crate) sender: UnboundedSender<SchedulerMsg>,
+}
+
+impl ScopeArena {
+    pub fn new(sender: UnboundedSender<SchedulerMsg>) -> Self {
+        Self {
+            bump: Bump::new(),
+            scopes: Vec::new(),
+            free_scopes: Vec::new(),
+            nodes: RefCell::new(Slab::new()),
+            sender,
+        }
+    }
+
+    pub fn get_scope(&self, id: &ScopeId) -> Option<&Scope> {
+        unsafe { Some(&*self.scopes[id.0]) }
+    }
+
+    pub fn new_with_key(
+        &mut self,
+        fc_ptr: *const (),
+        caller: *mut dyn Fn(&Scope) -> Element,
+        parent_scope: Option<*mut Scope>,
+        height: u32,
+        subtree: u32,
+    ) -> ScopeId {
+        if let Some(id) = self.free_scopes.pop() {
+            // have already called drop on it - the slot is still chillin tho
+            let scope = unsafe { &mut *self.scopes[id.0 as usize] };
+
+            todo!("override the scope contents");
+            id
+        } else {
+            let scope_id = ScopeId(self.scopes.len());
+
+            let old_root = NodeLink {
+                link_idx: 0,
+                gen_id: 0,
+                scope_id,
+            };
+            let new_root = NodeLink {
+                link_idx: 0,
+                gen_id: 0,
+                scope_id,
+            };
+
+            let new_scope = Scope {
+                sender: self.sender.clone(),
+                parent_scope,
+                our_arena_idx: scope_id,
+                height,
+                subtree: Cell::new(subtree),
+                is_subtree_root: Cell::new(false),
+                frames: [BumpFrame::new(), BumpFrame::new()],
+
+                hooks: Default::default(),
+                shared_contexts: Default::default(),
+                caller,
+                generation: 0.into(),
+
+                old_root: RefCell::new(Some(old_root)),
+                new_root: RefCell::new(Some(new_root)),
+                // old_root: RefCell::new(Some(old_root)),
+                // new_root: RefCell::new(None),
+                items: RefCell::new(SelfReferentialItems {
+                    listeners: Default::default(),
+                    borrowed_props: Default::default(),
+                    suspended_nodes: Default::default(),
+                    tasks: Default::default(),
+                    pending_effects: Default::default(),
+                }),
+            };
+
+            let stable = self.bump.alloc(new_scope);
+            self.scopes.push(stable);
+            scope_id
+        }
+    }
+
+    pub fn try_remove(&self, id: &ScopeId) -> Option<Scope> {
+        todo!()
+    }
+
+    pub fn reserve_node(&self, node: &VNode) -> ElementId {
+        let mut els = self.nodes.borrow_mut();
+        let entry = els.vacant_entry();
+        let key = entry.key();
+        let id = ElementId(key);
+        let node = node as *const _;
+        let node = unsafe { std::mem::transmute(node) };
+        entry.insert(node);
+        id
+
+        // let nodes = self.nodes.borrow_mut();
+        // let id = nodes.insert(());
+        // let node_id = ElementId(id);
+        // node = Some(node_id);
+        // node_id
+    }
+
+    pub fn collect_garbage(&self, id: ElementId) {
+        todo!()
+    }
+
+    // These methods would normally exist on `scope` but they need access to *all* of the scopes
+
+    /// This method cleans up any references to data held within our hook list. This prevents mutable aliasing from
+    /// causing UB in our tree.
+    ///
+    /// This works by cleaning up our references from the bottom of the tree to the top. The directed graph of components
+    /// essentially forms a dependency tree that we can traverse from the bottom to the top. As we traverse, we remove
+    /// any possible references to the data in the hook list.
+    ///
+    /// References to hook data can only be stored in listeners and component props. During diffing, we make sure to log
+    /// all listeners and borrowed props so we can clear them here.
+    ///
+    /// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
+    /// be dropped.
+    pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
+        let scope = self.get_scope(scope_id).unwrap();
+
+        // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
+        // run the hooks (which hold an &mut Reference)
+        // right now, we don't drop
+        scope
+            .items
+            .borrow_mut()
+            .borrowed_props
+            .drain(..)
+            .for_each(|comp| {
+                // First drop the component's undropped references
+                let scope_id = comp
+                    .associated_scope
+                    .get()
+                    .expect("VComponents should be associated with a valid Scope");
+
+                todo!("move this onto virtualdom");
+                // let scope = unsafe { &mut *scope_id };
+
+                // scope.ensure_drop_safety();
+
+                todo!("drop the component's props");
+                // let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
+                // drop_props();
+            });
+
+        // Now that all the references are gone, we can safely drop our own references in our listeners.
+        scope
+            .items
+            .borrow_mut()
+            .listeners
+            .drain(..)
+            .map(|li| unsafe { &*li })
+            .for_each(|listener| drop(listener.callback.borrow_mut().take()));
+    }
+
+    pub(crate) fn run_scope(&self, id: &ScopeId) -> bool {
+        let scope = self
+            .get_scope(id)
+            .expect("The base scope should never be moved");
+
+        // Cycle to the next frame and then reset it
+        // This breaks any latent references, invalidating every pointer referencing into it.
+        // Remove all the outdated listeners
+        self.ensure_drop_safety(id);
+
+        // Safety:
+        // - We dropped the listeners, so no more &mut T can be used while these are held
+        // - All children nodes that rely on &mut T are replaced with a new reference
+        unsafe { scope.hooks.reset() };
+
+        // Safety:
+        // - We've dropped all references to the wip bump frame with "ensure_drop_safety"
+        unsafe { scope.reset_wip_frame() };
+
+        {
+            let mut items = scope.items.borrow_mut();
+
+            // just forget about our suspended nodes while we're at it
+            items.suspended_nodes.clear();
+
+            // guarantee that we haven't screwed up - there should be no latent references anywhere
+            debug_assert!(items.listeners.is_empty());
+            debug_assert!(items.suspended_nodes.is_empty());
+            debug_assert!(items.borrowed_props.is_empty());
+
+            log::debug!("Borrowed stuff is successfully cleared");
+
+            // temporarily cast the vcomponent to the right lifetime
+            // let vcomp = scope.load_vcomp();
+        }
+
+        let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
+
+        // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
+        scope.wip_frame().nodes.borrow_mut().clear();
+        if let Some(key) = render(scope) {
+            dbg!(key);
+
+            dbg!(&scope.wip_frame().nodes.borrow_mut());
+            // let mut old = scope.old_root.borrow_mut();
+            // let mut new = scope.new_root.borrow_mut();
+
+            // let new_old = new.clone();
+            // *old = new_old;
+            // *new = Some(key);
+
+            // dbg!(&old);
+            // dbg!(&new);
+
+            // the user's component succeeded. We can safely cycle to the next frame
+            // scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
+            scope.cycle_frame();
+
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn wip_head(&self, id: &ScopeId) -> &VNode {
+        let scope = self.get_scope(id).unwrap();
+        let wip_frame = scope.wip_frame();
+        let nodes = wip_frame.nodes.borrow();
+        let node = nodes.get(0).unwrap();
+        unsafe { std::mem::transmute(node) }
+        // let root = scope.old_root.borrow();
+        // let link = root.as_ref().unwrap();
+        // dbg!(link);
+
+        // // for now, components cannot pass portals through each other
+        // assert_eq!(link.scope_id, *id);
+        // // assert_eq!(link.gen_id, scope.generation.get() - 1);
+
+        // // let items = scope.items.borrow();
+        // let nodes = scope.wip_frame().nodes.borrow();
+        // let node = nodes.get(link.link_idx).unwrap();
+        // unsafe { std::mem::transmute(node) }
+    }
+
+    pub fn fin_head(&self, id: &ScopeId) -> &VNode {
+        let scope = self.get_scope(id).unwrap();
+        let wip_frame = scope.fin_frame();
+        let nodes = wip_frame.nodes.borrow();
+        let node = nodes.get(0).unwrap();
+        unsafe { std::mem::transmute(node) }
+        // let scope = self.get_scope(id).unwrap();
+        // let root = scope.new_root.borrow();
+        // let link = root.as_ref().unwrap();
+
+        // // for now, components cannot pass portals through each other
+        // assert_eq!(link.scope_id, *id);
+        // // assert_eq!(link.gen_id, scope.generation.get());
+
+        // let nodes = scope.fin_frame().nodes.borrow();
+        // let node = nodes.get(link.link_idx).unwrap();
+        // unsafe { std::mem::transmute(node) }
+    }
+    pub fn root_node(&self, id: &ScopeId) -> &VNode {
+        self.wip_head(id)
+    }
+}

+ 0 - 39
packages/core/src/tasks.rs

@@ -1,39 +0,0 @@
-use crate::innerlude::*;
-use futures_channel::mpsc::UnboundedSender;
-
-pub struct TaskHandle {
-    pub(crate) sender: UnboundedSender<SchedulerMsg>,
-    pub(crate) our_id: u64,
-}
-
-impl TaskHandle {
-    /// Toggles this coroutine off/on.
-    ///
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn toggle(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
-            .unwrap()
-    }
-
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn resume(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ResumeTask(self.our_id)))
-            .unwrap()
-    }
-
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn stop(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
-            .unwrap()
-    }
-
-    /// This method is not synchronous - your task will not stop immediately.
-    pub fn restart(&self) {
-        self.sender
-            .unbounded_send(SchedulerMsg::Task(TaskMsg::ToggleTask(self.our_id)))
-            .unwrap()
-    }
-}

+ 26 - 15
packages/core/src/test_dom.rs

@@ -6,15 +6,16 @@ use crate::nodes::IntoVNode;
 
 
 pub struct TestDom {
 pub struct TestDom {
     bump: Bump,
     bump: Bump,
-    scheduler: Scheduler,
+    // scheduler: Scheduler,
 }
 }
 
 
 impl TestDom {
 impl TestDom {
     pub fn new() -> TestDom {
     pub fn new() -> TestDom {
         let bump = Bump::new();
         let bump = Bump::new();
         let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
         let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
-        let scheduler = Scheduler::new(sender, receiver);
-        TestDom { bump, scheduler }
+        todo!()
+        // let scheduler = Scheduler::new(sender, receiver, 10, 100);
+        // TestDom { bump, scheduler }
     }
     }
 
 
     pub fn new_factory(&self) -> NodeFactory {
     pub fn new_factory(&self) -> NodeFactory {
@@ -32,7 +33,9 @@ impl TestDom {
 
 
     pub fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
     pub fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
         let mutations = Mutations::new();
         let mutations = Mutations::new();
-        let mut machine = DiffMachine::new(mutations, &self.scheduler.pool);
+        let mut machine: DiffState = todo!();
+        // let mut machine = DiffState::new(mutations);
+        // let mut machine = DiffState::new(mutations);
         machine.stack.push(DiffInstruction::Diff { new, old });
         machine.stack.push(DiffInstruction::Diff { new, old });
         machine.mutations
         machine.mutations
     }
     }
@@ -40,13 +43,17 @@ impl TestDom {
     pub fn create<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
     pub fn create<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
         let old = self.bump.alloc(self.render_direct(left));
         let old = self.bump.alloc(self.render_direct(left));
 
 
-        let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
+        let mut machine: DiffState = todo!();
+        // let mut machine = DiffState::new(Mutations::new());
+        // let mut machine = DiffState::new(Mutations::new());
 
 
         machine.stack.create_node(old, MountType::Append);
         machine.stack.create_node(old, MountType::Append);
 
 
-        machine.work(&mut || false);
+        todo!()
 
 
-        machine.mutations
+        // machine.work(&mut || false);
+
+        // machine.mutations
     }
     }
 
 
     pub fn lazy_diff<'a>(
     pub fn lazy_diff<'a>(
@@ -56,22 +63,26 @@ impl TestDom {
     ) -> (Mutations<'a>, Mutations<'a>) {
     ) -> (Mutations<'a>, Mutations<'a>) {
         let (old, new) = (self.render(left), self.render(right));
         let (old, new) = (self.render(left), self.render(right));
 
 
-        let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
+        let mut machine: DiffState = todo!();
+        // let mut machine = DiffState::new(Mutations::new());
 
 
         machine.stack.create_node(old, MountType::Append);
         machine.stack.create_node(old, MountType::Append);
 
 
-        machine.work(|| false);
-        let create_edits = machine.mutations;
+        todo!()
+
+        // machine.work(|| false);
+        // let create_edits = machine.mutations;
 
 
-        let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
+        // let mut machine: DiffState = todo!();
+        // // let mut machine = DiffState::new(Mutations::new());
 
 
-        machine.stack.push(DiffInstruction::Diff { old, new });
+        // machine.stack.push(DiffInstruction::Diff { old, new });
 
 
-        machine.work(&mut || false);
+        // machine.work(&mut || false);
 
 
-        let edits = machine.mutations;
+        // let edits = machine.mutations;
 
 
-        (create_edits, edits)
+        // (create_edits, edits)
     }
     }
 }
 }
 
 

+ 0 - 57
packages/core/src/util.rs

@@ -7,63 +7,6 @@ pub fn empty_cell() -> Cell<Option<ElementId>> {
     Cell::new(None)
     Cell::new(None)
 }
 }
 
 
-pub fn type_name_of<T>(_: T) -> &'static str {
-    std::any::type_name::<T>()
-}
-
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
-
-// use crate::task::{Context, Poll};
-
-/// Cooperatively gives up a timeslice to the task scheduler.
-///
-/// Calling this function will move the currently executing future to the back
-/// of the execution queue, making room for other futures to execute. This is
-/// especially useful after running CPU-intensive operations inside a future.
-///
-/// See also [`task::spawn_blocking`].
-///
-/// [`task::spawn_blocking`]: fn.spawn_blocking.html
-///
-/// # Examples
-///
-/// Basic usage:
-///
-/// ```
-/// # async_std::task::block_on(async {
-/// #
-/// use async_std::task;
-///
-/// task::yield_now().await;
-/// #
-/// # })
-/// ```
-#[inline]
-pub async fn yield_now() {
-    YieldNow(false).await
-}
-
-struct YieldNow(bool);
-
-impl Future for YieldNow {
-    type Output = ();
-
-    // The futures executor is implemented as a FIFO queue, so all this future
-    // does is re-schedule the future back to the end of the queue, giving room
-    // for other futures to progress.
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        if !self.0 {
-            self.0 = true;
-            cx.waker().wake_by_ref();
-            Poll::Pending
-        } else {
-            Poll::Ready(())
-        }
-    }
-}
-
 /// A component's unique identifier.
 /// A component's unique identifier.
 ///
 ///
 /// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
 /// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is

+ 361 - 164
packages/core/src/virtual_dom.rs

@@ -19,10 +19,14 @@
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! Additional functionality is defined in the respective files.
 //! Additional functionality is defined in the respective files.
 
 
-use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
-
 use crate::innerlude::*;
 use crate::innerlude::*;
-use std::{any::Any, rc::Rc};
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+use futures_util::{Future, StreamExt};
+use fxhash::FxHashSet;
+use indexmap::IndexSet;
+use std::pin::Pin;
+use std::task::Poll;
+use std::{any::Any, collections::VecDeque};
 
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 /// An integrated virtual node system that progresses events and diffs UI trees.
 ///
 ///
@@ -55,18 +59,22 @@ use std::{any::Any, rc::Rc};
 /// }
 /// }
 /// ```
 /// ```
 pub struct VirtualDom {
 pub struct VirtualDom {
-    scheduler: Scheduler,
-
     base_scope: ScopeId,
     base_scope: ScopeId,
 
 
-    root_fc: Box<dyn Any>,
+    _root_caller: *mut dyn Fn(&Scope) -> Element,
 
 
-    root_props: Rc<dyn Any>,
+    pub(crate) scopes: ScopeArena,
 
 
-    // we need to keep the allocation around, but we don't necessarily use it
-    _root_caller: Box<dyn Any>,
+    receiver: UnboundedReceiver<SchedulerMsg>,
+    pub(crate) sender: UnboundedSender<SchedulerMsg>,
+
+    // Every component that has futures that need to be polled
+    pending_futures: FxHashSet<ScopeId>,
+    pending_messages: VecDeque<SchedulerMsg>,
+    dirty_scopes: IndexSet<ScopeId>,
 }
 }
 
 
+// Methods to create the VirtualDom
 impl VirtualDom {
 impl VirtualDom {
     /// Create a new VirtualDOM with a component that does not have special props.
     /// Create a new VirtualDOM with a component that does not have special props.
     ///
     ///
@@ -127,7 +135,7 @@ impl VirtualDom {
         Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
         Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
     }
     }
 
 
-    /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler.
+    /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler
     ///
     ///
     /// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
     /// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
     /// VirtualDom to be created just to retrieve its channel receiver.
     /// VirtualDom to be created just to retrieve its channel receiver.
@@ -137,98 +145,281 @@ impl VirtualDom {
         sender: UnboundedSender<SchedulerMsg>,
         sender: UnboundedSender<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
     ) -> Self {
     ) -> Self {
-        let root_fc = Box::new(root);
-
-        let root_props: Rc<dyn Any> = Rc::new(root_props);
+        let mut scopes = ScopeArena::new(sender.clone());
 
 
-        let props = root_props.clone();
-
-        let root_caller: Box<dyn Fn(&ScopeInner) -> Element> =
-            Box::new(move |scope: &ScopeInner| {
-                let props = props.downcast_ref::<P>().unwrap();
-                let node = root((scope, props));
-                // cast into the right lifetime
-                unsafe { std::mem::transmute(node) }
-            });
-        let caller = unsafe { bumpalo::boxed::Box::from_raw(root_caller.as_mut() as *mut _) };
-
-        let scheduler = Scheduler::new(sender, receiver);
-
-        let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
-            ScopeInner::new(caller, myidx, None, 0, 0, scheduler.pool.channel.clone())
-        });
+        let caller = Box::new(move |f: &Scope| -> Element { root(f, &root_props) });
+        let caller_ref: *mut dyn Fn(&Scope) -> Element = Box::into_raw(caller);
+        let base_scope = scopes.new_with_key(root as _, caller_ref, None, 0, 0);
 
 
         Self {
         Self {
-            _root_caller: Box::new(root_caller),
-            root_fc,
+            scopes,
             base_scope,
             base_scope,
-            scheduler,
-            root_props,
+            receiver,
+            // todo: clean this up manually?
+            _root_caller: caller_ref,
+            pending_messages: VecDeque::new(),
+            pending_futures: Default::default(),
+            dirty_scopes: Default::default(),
+            sender,
         }
         }
     }
     }
 
 
-    /// Get the [`Scope`] for the root component.
+    /// Get the [`ScopeState`] for the root component.
     ///
     ///
     /// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
     /// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
     /// directly.
     /// directly.
-    pub fn base_scope(&self) -> &ScopeInner {
-        self.scheduler.pool.get_scope(self.base_scope).unwrap()
+    ///
+    /// # Example
+    pub fn base_scope(&self) -> &Scope {
+        self.get_scope(&self.base_scope).unwrap()
     }
     }
 
 
-    /// Get the [`Scope`] for a component given its [`ScopeId`]
-    pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeInner> {
-        self.scheduler.pool.get_scope(id)
+    /// Get the [`ScopeState`] for a component given its [`ScopeId`]
+    ///
+    /// # Example
+    ///
+    ///
+    ///
+    pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a Scope> {
+        self.scopes.get_scope(id)
     }
     }
 
 
-    /// Update the root props of this VirtualDOM.
+    /// Get an [`UnboundedSender`] handle to the channel used by the scheduler.
     ///
     ///
-    /// This method returns None if the old props could not be removed. The entire VirtualDOM will be rebuilt immediately,
-    /// so calling this method will block the main thread until computation is done.
+    /// # Example
     ///
     ///
-    /// ## Example
     ///
     ///
-    /// ```rust
-    /// #[derive(Props, PartialEq)]
-    /// struct AppProps {
-    ///     route: &'static str
-    /// }
-    /// static App: FC<AppProps> = |(cx, props)|cx.render(rsx!{ "route is {cx.route}" });
+    ///    
+    pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
+        self.sender.clone()
+    }
+
+    /// Check if the [`VirtualDom`] has any pending updates or work to be done.
+    ///
+    /// # Example
     ///
     ///
-    /// let mut dom = VirtualDom::new_with_props(App, AppProps { route: "start" });
     ///
     ///
-    /// let mutations = dom.update_root_props(AppProps { route: "end" }).unwrap();
+    ///
+    pub fn has_any_work(&self) -> bool {
+        !(self.dirty_scopes.is_empty() && self.pending_messages.is_empty())
+    }
+
+    /// Waits for the scheduler to have work
+    /// This lets us poll async tasks during idle periods without blocking the main thread.
+    pub async fn wait_for_work(&mut self) {
+        // todo: poll the events once even if there is work to do to prevent starvation
+
+        // if there's no futures in the virtualdom, just wait for a scheduler message and put it into the queue to be processed
+        if self.pending_futures.is_empty() {
+            self.pending_messages
+                .push_front(self.receiver.next().await.unwrap());
+        } else {
+            struct PollTasks<'a> {
+                pending_futures: &'a FxHashSet<ScopeId>,
+                scopes: &'a ScopeArena,
+            }
+
+            impl<'a> Future for PollTasks<'a> {
+                type Output = ();
+
+                fn poll(
+                    self: Pin<&mut Self>,
+                    cx: &mut std::task::Context<'_>,
+                ) -> Poll<Self::Output> {
+                    let mut all_pending = true;
+
+                    // Poll every scope manually
+                    for fut in self.pending_futures.iter() {
+                        let scope = self
+                            .scopes
+                            .get_scope(fut)
+                            .expect("Scope should never be moved");
+
+                        let mut items = scope.items.borrow_mut();
+                        for task in items.tasks.iter_mut() {
+                            let task = task.as_mut();
+
+                            // todo: does this make sense?
+                            // I don't usually write futures by hand
+                            // I think the futures neeed to be pinned using bumpbox or something
+                            // right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
+                            let unpinned = unsafe { Pin::new_unchecked(task) };
+
+                            if unpinned.poll(cx).is_ready() {
+                                all_pending = false
+                            }
+                        }
+                    }
+
+                    // Resolve the future if any singular task is ready
+                    match all_pending {
+                        true => Poll::Pending,
+                        false => Poll::Ready(()),
+                    }
+                }
+            }
+
+            // Poll both the futures and the scheduler message queue simulataneously
+            use futures_util::future::{select, Either};
+
+            let scheduler_fut = self.receiver.next();
+            let tasks_fut = PollTasks {
+                pending_futures: &self.pending_futures,
+                scopes: &self.scopes,
+            };
+
+            match select(tasks_fut, scheduler_fut).await {
+                // Futures don't generate work
+                Either::Left((_, _)) => {}
+
+                // Save these messages in FIFO to be processed later
+                Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
+            }
+        }
+    }
+
+    /// Run the virtualdom with a deadline.
+    ///
+    /// This method will progress async tasks until the deadline is reached. If tasks are completed before the deadline,
+    /// and no tasks are pending, this method will return immediately. If tasks are still pending, then this method will
+    /// exhaust the deadline working on them.
+    ///
+    /// This method is useful when needing to schedule the virtualdom around other tasks on the main thread to prevent
+    /// "jank". It will try to finish whatever work it has by the deadline to free up time for other work.
+    ///
+    /// Due to platform differences in how time is handled, this method accepts a future that resolves when the deadline
+    /// is exceeded. However, the deadline won't be met precisely, so you might want to build some wiggle room into the
+    /// deadline closure manually.
+    ///
+    /// The deadline is polled before starting to diff components. This strikes a balance between the overhead of checking
+    /// the deadline and just completing the work. However, if an individual component takes more than 16ms to render, then
+    /// the screen will "jank" up. In debug, this will trigger an alert.
+    ///
+    /// If there are no in-flight fibers when this method is called, it will await any possible tasks, aborting early if
+    /// the provided deadline future resolves.
+    ///
+    /// For use in the web, it is expected that this method will be called to be executed during "idle times" and the
+    /// mutations to be applied during the "paint times" IE "animation frames". With this strategy, it is possible to craft
+    /// entirely jank-free applications that perform a ton of work.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// static App: FC<()> = |(cx, props)|rsx!(cx, div {"hello"} );
+    ///
+    /// let mut dom = VirtualDom::new(App);
+    ///
+    /// loop {
+    ///     let mut timeout = TimeoutFuture::from_ms(16);
+    ///     let deadline = move || (&mut timeout).now_or_never();
+    ///
+    ///     let mutations = dom.run_with_deadline(deadline).await;
+    ///
+    ///     apply_mutations(mutations);
+    /// }
     /// ```
     /// ```
-    pub fn update_root_props<P>(&mut self, root_props: P) -> Option<Mutations>
-    where
-        P: 'static,
-    {
-        let root_scope = self.scheduler.pool.get_scope_mut(self.base_scope).unwrap();
+    ///
+    /// ## Mutations
+    ///
+    /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
+    /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
+    /// applied the edits.
+    ///
+    /// Mutations are the only link between the RealDOM and the VirtualDOM.
+    pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
+        let mut committed_mutations = vec![];
 
 
-        // Pre-emptively drop any downstream references of the old props
-        root_scope.ensure_drop_safety(&self.scheduler.pool);
+        while self.has_any_work() {
+            while let Ok(Some(msg)) = self.receiver.try_next() {
+                self.pending_messages.push_front(msg);
+            }
 
 
-        let mut root_props: Rc<dyn Any> = Rc::new(root_props);
+            while let Some(msg) = self.pending_messages.pop_back() {
+                match msg {
+                    SchedulerMsg::Immediate(id) => {
+                        self.dirty_scopes.insert(id);
+                    }
+                    SchedulerMsg::UiEvent(event) => {
+                        if let Some(element) = event.mounted_dom_id {
+                            log::info!("Calling listener {:?}, {:?}", event.scope_id, element);
+
+                            let scope = self.scopes.get_scope(&event.scope_id).unwrap();
+
+                            // TODO: bubble properly here
+                            scope.call_listener(event, element);
+
+                            while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
+                                self.pending_messages.push_front(dirty_scope);
+                            }
+                        } else {
+                            log::debug!("User event without a targetted ElementId. Not currently supported.\nUnsure how to proceed. {:?}", event);
+                        }
+                    }
+                }
+            }
 
 
-        if let Some(props_ptr) = root_props.downcast_ref::<P>().map(|p| p as *const P) {
-            // Swap the old props and new props
-            std::mem::swap(&mut self.root_props, &mut root_props);
+            let mut diff_state: DiffState = DiffState::new(Mutations::new());
 
 
-            let root = *self.root_fc.downcast_ref::<FC<P>>().unwrap();
+            let mut ran_scopes = FxHashSet::default();
 
 
-            let root_caller: Box<dyn Fn(&ScopeInner) -> Element> =
-                Box::new(move |scope: &ScopeInner| unsafe {
-                    let props: &'_ P = &*(props_ptr as *const P);
-                    std::mem::transmute(root((scope, props)))
-                });
+            // todo: the 2021 version of rust will let us not have to force the borrow
+            let scopes = &self.scopes;
 
 
-            root_scope.update_scope_dependencies(&root_caller);
+            // Sort the scopes by height. Theoretically, we'll de-duplicate scopes by height
+            self.dirty_scopes
+                .retain(|id| scopes.get_scope(id).is_some());
 
 
-            drop(root_props);
+            self.dirty_scopes.sort_by(|a, b| {
+                let h1 = scopes.get_scope(a).unwrap().height;
+                let h2 = scopes.get_scope(b).unwrap().height;
+                h1.cmp(&h2).reverse()
+            });
 
 
-            Some(self.rebuild())
-        } else {
-            None
+            if let Some(scopeid) = self.dirty_scopes.pop() {
+                log::info!("handling dirty scope {:?}", scopeid);
+
+                if !ran_scopes.contains(&scopeid) {
+                    ran_scopes.insert(scopeid);
+
+                    log::debug!("about to run scope {:?}", scopeid);
+
+                    if self.scopes.run_scope(&scopeid) {
+                        let (old, new) = (
+                            self.scopes.wip_head(&scopeid),
+                            self.scopes.fin_head(&scopeid),
+                        );
+                        diff_state.stack.scope_stack.push(scopeid);
+                        diff_state.stack.push(DiffInstruction::Diff { new, old });
+                    }
+                }
+            }
+
+            let work_completed = self.scopes.work(&mut diff_state, &mut deadline);
+
+            if work_completed {
+                let DiffState {
+                    mutations,
+                    seen_scopes,
+                    stack,
+                    ..
+                } = diff_state;
+
+                for scope in seen_scopes {
+                    self.dirty_scopes.remove(&scope);
+                }
+
+                // I think the stack should be empty at the end of diffing?
+                debug_assert_eq!(stack.scope_stack.len(), 0);
+
+                committed_mutations.push(mutations);
+            } else {
+                // leave the work in an incomplete state
+                log::debug!("don't have a mechanism to pause work (yet)");
+                return committed_mutations;
+            }
         }
         }
+
+        committed_mutations
     }
     }
 
 
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch
@@ -242,14 +433,27 @@ impl VirtualDom {
     ///
     ///
     /// # Example
     /// # Example
     /// ```
     /// ```
-    /// static App: FC<()> = |(cx, props)|cx.render(rsx!{ "hello world" });
+    /// static App: FC<()> = |(cx, props)| cx.render(rsx!{ "hello world" });
     /// let mut dom = VirtualDom::new();
     /// let mut dom = VirtualDom::new();
     /// let edits = dom.rebuild();
     /// let edits = dom.rebuild();
     ///
     ///
     /// apply_edits(edits);
     /// apply_edits(edits);
     /// ```
     /// ```
     pub fn rebuild(&mut self) -> Mutations {
     pub fn rebuild(&mut self) -> Mutations {
-        self.scheduler.rebuild(self.base_scope)
+        let mut diff_machine = DiffState::new(Mutations::new());
+
+        let scope_id = self.base_scope;
+        if self.scopes.run_scope(&scope_id) {
+            diff_machine
+                .stack
+                .create_node(self.scopes.fin_head(&scope_id), MountType::Append);
+
+            diff_machine.stack.scope_stack.push(scope_id);
+
+            self.scopes.work(&mut diff_machine, || false);
+        }
+
+        diff_machine.mutations
     }
     }
 
 
     /// Compute a manual diff of the VirtualDOM between states.
     /// Compute a manual diff of the VirtualDOM between states.
@@ -286,107 +490,100 @@ impl VirtualDom {
     ///
     ///
     /// let edits = dom.diff();
     /// let edits = dom.diff();
     /// ```
     /// ```
-    pub fn diff(&mut self) -> Mutations {
-        self.scheduler.hard_diff(self.base_scope)
-    }
+    pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
+        log::debug!("hard diff {:?}", scope_id);
 
 
-    /// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
-    ///
-    /// This method will not wait for any suspended nodes to complete. If there is no pending work, then this method will
-    /// return "None"
-    pub fn run_immediate(&mut self) -> Option<Vec<Mutations>> {
-        if self.scheduler.has_any_work() {
-            Some(self.scheduler.work_sync())
+        if self.scopes.run_scope(scope_id) {
+            let mut diff_machine = DiffState::new(Mutations::new());
+
+            diff_machine.force_diff = true;
+
+            self.scopes.diff_scope(&mut diff_machine, scope_id);
+
+            dbg!(&diff_machine.mutations);
+
+            Some(diff_machine.mutations)
         } else {
         } else {
             None
             None
         }
         }
     }
     }
+}
 
 
-    /// Run the virtualdom with a deadline.
-    ///
-    /// This method will progress async tasks until the deadline is reached. If tasks are completed before the deadline,
-    /// and no tasks are pending, this method will return immediately. If tasks are still pending, then this method will
-    /// exhaust the deadline working on them.
-    ///
-    /// This method is useful when needing to schedule the virtualdom around other tasks on the main thread to prevent
-    /// "jank". It will try to finish whatever work it has by the deadline to free up time for other work.
-    ///
-    /// Due to platform differences in how time is handled, this method accepts a future that resolves when the deadline
-    /// is exceeded. However, the deadline won't be met precisely, so you might want to build some wiggle room into the
-    /// deadline closure manually.
+pub enum SchedulerMsg {
+    // events from the host
+    UiEvent(UserEvent),
+
+    // setstate
+    Immediate(ScopeId),
+}
+
+#[derive(Debug)]
+pub struct UserEvent {
+    /// The originator of the event trigger
+    pub scope_id: ScopeId,
+
+    pub priority: EventPriority,
+
+    /// The optional real node associated with the trigger
+    pub mounted_dom_id: Option<ElementId>,
+
+    /// The event type IE "onclick" or "onmouseover"
     ///
     ///
-    /// The deadline is polled before starting to diff components. This strikes a balance between the overhead of checking
-    /// the deadline and just completing the work. However, if an individual component takes more than 16ms to render, then
-    /// the screen will "jank" up. In debug, this will trigger an alert.
+    /// The name that the renderer will use to mount the listener.
+    pub name: &'static str,
+
+    /// Event Data
+    pub event: Box<dyn Any + Send>,
+}
+
+/// Priority of Event Triggers.
+///
+/// Internally, Dioxus will abort work that's taking too long if new, more important work arrives. Unlike React, Dioxus
+/// won't be afraid to pause work or flush changes to the RealDOM. This is called "cooperative scheduling". Some Renderers
+/// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
+///
+/// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes.
+///
+/// React has a 5-tier priority system. However, they break things into "Continuous" and "Discrete" priority. For now,
+/// we keep it simple, and just use a 3-tier priority system.
+///
+/// - NoPriority = 0
+/// - LowPriority = 1
+/// - NormalPriority = 2
+/// - UserBlocking = 3
+/// - HighPriority = 4
+/// - ImmediatePriority = 5
+///
+/// We still have a concept of discrete vs continuous though - discrete events won't be batched, but continuous events will.
+/// This means that multiple "scroll" events will be processed in a single frame, but multiple "click" events will be
+/// flushed before proceeding. Multiple discrete events is highly unlikely, though.
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
+pub enum EventPriority {
+    /// Work that must be completed during the EventHandler phase.
     ///
     ///
-    /// If there are no in-flight fibers when this method is called, it will await any possible tasks, aborting early if
-    /// the provided deadline future resolves.
+    /// Currently this is reserved for controlled inputs.
+    Immediate = 3,
+
+    /// "High Priority" work will not interrupt other high priority work, but will interrupt medium and low priority work.
     ///
     ///
-    /// For use in the web, it is expected that this method will be called to be executed during "idle times" and the
-    /// mutations to be applied during the "paint times" IE "animation frames". With this strategy, it is possible to craft
-    /// entirely jank-free applications that perform a ton of work.
+    /// This is typically reserved for things like user interaction.
     ///
     ///
-    /// # Example
+    /// React calls these "discrete" events, but with an extra category of "user-blocking" (Immediate).
+    High = 2,
+
+    /// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
+    /// than "High Priority" events and will take precedence over low priority events.
     ///
     ///
-    /// ```no_run
-    /// static App: FC<()> = |(cx, props)|rsx!(cx, div {"hello"} );
-    /// let mut dom = VirtualDom::new(App);
-    /// loop {
-    ///     let deadline = TimeoutFuture::from_ms(16);
-    ///     let mutations = dom.run_with_deadline(deadline).await;
-    ///     apply_mutations(mutations);
-    /// }
-    /// ```
+    /// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
     ///
     ///
-    /// ## Mutations
+    /// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc).
+    Medium = 1,
+
+    /// "Low Priority" work will always be preempted unless the work is significantly delayed, in which case it will be
+    /// advanced to the front of the work queue until completed.
     ///
     ///
-    /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
-    /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
-    /// applied the edits.
+    /// The primary user of Low Priority work is the asynchronous work system (Suspense).
     ///
     ///
-    /// Mutations are the only link between the RealDOM and the VirtualDOM.
-    pub fn run_with_deadline(&mut self, deadline: impl FnMut() -> bool) -> Vec<Mutations<'_>> {
-        self.scheduler.work_with_deadline(deadline)
-    }
-
-    pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
-        self.scheduler.pool.channel.sender.clone()
-    }
-
-    /// Waits for the scheduler to have work
-    /// This lets us poll async tasks during idle periods without blocking the main thread.
-    pub async fn wait_for_work(&mut self) {
-        // todo: poll the events once even if there is work to do to prevent starvation
-        if self.scheduler.has_any_work() {
-            return;
-        }
-
-        use futures_util::StreamExt;
-
-        // Wait for any new events if we have nothing to do
-
-        let tasks_fut = self.scheduler.async_tasks.next();
-        let scheduler_fut = self.scheduler.receiver.next();
-
-        use futures_util::future::{select, Either};
-        match select(tasks_fut, scheduler_fut).await {
-            // poll the internal futures
-            Either::Left((_id, _)) => {
-                //
-            }
-
-            // wait for an external event
-            Either::Right((msg, _)) => match msg.unwrap() {
-                SchedulerMsg::Task(t) => {
-                    self.scheduler.handle_task(t);
-                }
-                SchedulerMsg::Immediate(im) => {
-                    self.scheduler.dirty_scopes.insert(im);
-                }
-                SchedulerMsg::UiEvent(evt) => {
-                    self.scheduler.ui_events.push_back(evt);
-                }
-            },
-        }
-    }
+    /// This is considered "idle" work or "background" work.
+    Low = 0,
 }
 }

+ 4 - 4
packages/core/tests/borrowedstate.rs

@@ -10,8 +10,8 @@ fn test_borrowed_state() {
     let _ = VirtualDom::new(Parent);
     let _ = VirtualDom::new(Parent);
 }
 }
 
 
-fn Parent((cx, _): Scope<()>) -> Element {
-    let value = cx.use_hook(|_| String::new(), |f| &*f, |_| {});
+fn Parent(cx: Context, props: &()) -> Element {
+    let value = cx.use_hook(|_| String::new(), |f| &*f);
 
 
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {
@@ -28,7 +28,7 @@ struct ChildProps<'a> {
     name: &'a str,
     name: &'a str,
 }
 }
 
 
-fn Child<'a>((cx, props): Scope<'a, ChildProps>) -> Element<'a> {
+fn Child(cx: Context, props: &ChildProps) -> Element {
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {
             h1 { "it's nested" }
             h1 { "it's nested" }
@@ -42,7 +42,7 @@ struct Grandchild<'a> {
     name: &'a str,
     name: &'a str,
 }
 }
 
 
-fn Child2<'a>((cx, props): Scope<'a, Grandchild>) -> Element<'a> {
+fn Child2(cx: Context, props: &Grandchild) -> Element {
     cx.render(rsx! {
     cx.render(rsx! {
         div { "Hello {props.name}!" }
         div { "Hello {props.name}!" }
     })
     })

+ 22 - 21
packages/core/tests/create_dom.rs

@@ -21,7 +21,7 @@ fn new_dom<P: 'static + Send>(app: FC<P>, props: P) -> VirtualDom {
 
 
 #[test]
 #[test]
 fn test_original_diff() {
 fn test_original_diff() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             div {
             div {
                 div {
                 div {
@@ -57,17 +57,17 @@ fn test_original_diff() {
 
 
 #[test]
 #[test]
 fn create() {
 fn create() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             div {
             div {
                 div {
                 div {
                     "Hello, world!"
                     "Hello, world!"
                     div {
                     div {
                         div {
                         div {
-                            // Fragment {
-                            //     "hello"
-                            //     "world"
-                            // }
+                            Fragment {
+                                "hello"
+                                "world"
+                            }
                         }
                         }
                     }
                     }
                 }
                 }
@@ -120,7 +120,7 @@ fn create() {
 
 
 #[test]
 #[test]
 fn create_list() {
 fn create_list() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             {(0..3).map(|f| rsx!{ div {
             {(0..3).map(|f| rsx!{ div {
                 "hello"
                 "hello"
@@ -169,7 +169,7 @@ fn create_list() {
 
 
 #[test]
 #[test]
 fn create_simple() {
 fn create_simple() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             div {}
             div {}
             div {}
             div {}
@@ -207,7 +207,7 @@ fn create_simple() {
 }
 }
 #[test]
 #[test]
 fn create_components() {
 fn create_components() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             Child { "abc1" }
             Child { "abc1" }
             Child { "abc2" }
             Child { "abc2" }
@@ -220,7 +220,7 @@ fn create_components() {
         children: ScopeChildren<'a>,
         children: ScopeChildren<'a>,
     }
     }
 
 
-    fn Child<'a>((cx, props): Scope<'a, ChildProps<'a>>) -> Element {
+    fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element {
         cx.render(rsx! {
         cx.render(rsx! {
             h1 {}
             h1 {}
             div { {&props.children} }
             div { {&props.children} }
@@ -273,7 +273,7 @@ fn create_components() {
 }
 }
 #[test]
 #[test]
 fn anchors() {
 fn anchors() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             {true.then(|| rsx!{ div { "hello" } })}
             {true.then(|| rsx!{ div { "hello" } })}
             {false.then(|| rsx!{ div { "goodbye" } })}
             {false.then(|| rsx!{ div { "goodbye" } })}
@@ -302,17 +302,18 @@ fn anchors() {
 
 
 #[test]
 #[test]
 fn suspended() {
 fn suspended() {
-    static App: FC<()> = |(cx, props)| {
-        let val = use_suspense(cx, || async {}, |cx, p| todo!());
+    todo!()
+    // static App: FC<()> = |cx, props| {
+    //     let val = use_suspense(cx, || async {}, |p| todo!());
 
 
-        cx.render(rsx! { {val} })
-    };
+    //     cx.render(rsx! { {val} })
+    // };
 
 
-    let mut dom = new_dom(App, ());
-    let mutations = dom.rebuild();
+    // let mut dom = new_dom(App, ());
+    // let mutations = dom.rebuild();
 
 
-    assert_eq!(
-        mutations.edits,
-        [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
-    );
+    // assert_eq!(
+    //     mutations.edits,
+    //     [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
+    // );
 }
 }

+ 1 - 1
packages/core/tests/diffing.rs

@@ -5,7 +5,7 @@
 //!
 //!
 //! It does not validated that component lifecycles work properly. This is done in another test file.
 //! It does not validated that component lifecycles work properly. This is done in another test file.
 
 
-use dioxus::{nodes::VSuspended, prelude::*, DomEdit, TestDom};
+use dioxus::{prelude::*, DomEdit, TestDom, VSuspended};
 use dioxus_core as dioxus;
 use dioxus_core as dioxus;
 use dioxus_core_macro::*;
 use dioxus_core_macro::*;
 use dioxus_html as dioxus_elements;
 use dioxus_html as dioxus_elements;

+ 2 - 4
packages/core/tests/display_vdom.rs

@@ -13,7 +13,7 @@ mod test_logging;
 
 
 #[test]
 #[test]
 fn please_work() {
 fn please_work() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             div {
             div {
                 hidden: "true"
                 hidden: "true"
@@ -27,7 +27,7 @@ fn please_work() {
         })
         })
     };
     };
 
 
-    static Child: FC<()> = |(cx, props)| {
+    static Child: FC<()> = |cx, props| {
         cx.render(rsx! {
         cx.render(rsx! {
             div { "child" }
             div { "child" }
         })
         })
@@ -35,6 +35,4 @@ fn please_work() {
 
 
     let mut dom = VirtualDom::new(App);
     let mut dom = VirtualDom::new(App);
     dom.rebuild();
     dom.rebuild();
-
-    println!("{}", dom);
 }
 }

+ 2 - 2
packages/core/tests/lifecycle.rs

@@ -20,7 +20,7 @@ fn manual_diffing() {
         value: Shared<&'static str>,
         value: Shared<&'static str>,
     }
     }
 
 
-    static App: FC<AppProps> = |(cx, props)| {
+    static App: FC<AppProps> = |cx, props| {
         let val = props.value.lock().unwrap();
         let val = props.value.lock().unwrap();
         cx.render(rsx! { div { "{val}" } })
         cx.render(rsx! { div { "{val}" } })
     };
     };
@@ -37,7 +37,7 @@ fn manual_diffing() {
 
 
     *value.lock().unwrap() = "goodbye";
     *value.lock().unwrap() = "goodbye";
 
 
-    let edits = dom.diff();
+    let edits = dom.rebuild();
 
 
     log::debug!("edits: {:?}", edits);
     log::debug!("edits: {:?}", edits);
 }
 }

+ 2 - 2
packages/core/tests/sharedstate.rs

@@ -13,12 +13,12 @@ mod test_logging;
 fn shared_state_test() {
 fn shared_state_test() {
     struct MySharedState(&'static str);
     struct MySharedState(&'static str);
 
 
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.provide_state(MySharedState("world!"));
         cx.provide_state(MySharedState("world!"));
         cx.render(rsx!(Child {}))
         cx.render(rsx!(Child {}))
     };
     };
 
 
-    static Child: FC<()> = |(cx, props)| {
+    static Child: FC<()> = |cx, props| {
         let shared = cx.consume_state::<MySharedState>()?;
         let shared = cx.consume_state::<MySharedState>()?;
         cx.render(rsx!("Hello, {shared.0}"))
         cx.render(rsx!("Hello, {shared.0}"))
     };
     };

+ 15 - 14
packages/core/tests/vdom_rebuild.rs

@@ -17,7 +17,7 @@ use dioxus_html as dioxus_elements;
 
 
 #[test]
 #[test]
 fn app_runs() {
 fn app_runs() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         //
         //
         cx.render(rsx!( div{"hello"} ))
         cx.render(rsx!( div{"hello"} ))
     };
     };
@@ -28,7 +28,7 @@ fn app_runs() {
 
 
 #[test]
 #[test]
 fn fragments_work() {
 fn fragments_work() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
         cx.render(rsx!(
             div{"hello"}
             div{"hello"}
             div{"goodbye"}
             div{"goodbye"}
@@ -42,7 +42,7 @@ fn fragments_work() {
 
 
 #[test]
 #[test]
 fn lists_work() {
 fn lists_work() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
         cx.render(rsx!(
             h1 {"hello"}
             h1 {"hello"}
             {(0..6).map(|f| rsx!(span{ "{f}" }))}
             {(0..6).map(|f| rsx!(span{ "{f}" }))}
@@ -55,7 +55,7 @@ fn lists_work() {
 
 
 #[test]
 #[test]
 fn conditional_rendering() {
 fn conditional_rendering() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
         cx.render(rsx!(
             h1 {"hello"}
             h1 {"hello"}
             {true.then(|| rsx!(span{ "a" }))}
             {true.then(|| rsx!(span{ "a" }))}
@@ -72,13 +72,13 @@ fn conditional_rendering() {
 
 
 #[test]
 #[test]
 fn child_components() {
 fn child_components() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
         cx.render(rsx!(
             {true.then(|| rsx!(Child { }))}
             {true.then(|| rsx!(Child { }))}
             {false.then(|| rsx!(Child { }))}
             {false.then(|| rsx!(Child { }))}
         ))
         ))
     };
     };
-    static Child: FC<()> = |(cx, props)| {
+    static Child: FC<()> = |cx, props| {
         cx.render(rsx!(
         cx.render(rsx!(
             h1 {"hello"}
             h1 {"hello"}
             h1 {"goodbye"}
             h1 {"goodbye"}
@@ -91,13 +91,14 @@ fn child_components() {
 
 
 #[test]
 #[test]
 fn suspended_works() {
 fn suspended_works() {
-    static App: FC<()> = |(cx, props)| {
-        let title = use_suspense(cx, || async { "bob" }, move |cx, f| todo!());
-        // let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
-        cx.render(rsx!("hello" { title }))
-    };
+    todo!()
+    // static App: FC<()> = |cx, props| {
+    //     let title = use_suspense(cx, || async { "bob" }, move |cx, f| todo!());
+    //     // let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
+    //     cx.render(rsx!("hello" { title }))
+    // };
 
 
-    let mut vdom = VirtualDom::new(App);
-    let edits = vdom.rebuild();
-    dbg!(edits);
+    // let mut vdom = VirtualDom::new(App);
+    // let edits = vdom.rebuild();
+    // dbg!(edits);
 }
 }

+ 1 - 1
packages/desktop/Cargo.toml

@@ -24,6 +24,7 @@ tokio = { version = "1.12.0", features = [
     "rt",
     "rt",
 ], optional = true, default-features = false }
 ], optional = true, default-features = false }
 dioxus-core-macro = { path = "../core-macro" }
 dioxus-core-macro = { path = "../core-macro" }
+dioxus-html = { path = "../html", features = ["serialize"] }
 
 
 [features]
 [features]
 default = ["tokio_runtime"]
 default = ["tokio_runtime"]
@@ -31,6 +32,5 @@ tokio_runtime = ["tokio"]
 
 
 
 
 [dev-dependencies]
 [dev-dependencies]
-dioxus-html = { path = "../html" }
 dioxus-hooks = { path = "../hooks" }
 dioxus-hooks = { path = "../hooks" }
 simple_logger = "1.13.0"
 simple_logger = "1.13.0"

+ 1 - 4
packages/desktop/src/desktop_context.rs

@@ -57,7 +57,7 @@ pub struct WebviewWindowProps<'a> {
 ///
 ///
 ///
 ///
 ///
 ///
-pub fn WebviewWindow<'a>((cx, props): Scope<'a, WebviewWindowProps>) -> Element<'a> {
+pub fn WebviewWindow((cx, props): Scope<WebviewWindowProps>) -> Element {
     let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
     let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
 
 
     cx.use_hook(
     cx.use_hook(
@@ -67,9 +67,6 @@ pub fn WebviewWindow<'a>((cx, props): Scope<'a, WebviewWindowProps>) -> Element<
         |state| {
         |state| {
             //
             //
         },
         },
-        |hook| {
-            //
-        },
     );
     );
 
 
     // render the children directly
     // render the children directly

+ 1 - 0
packages/desktop/src/err.rs

@@ -0,0 +1 @@
+

+ 6 - 6
packages/desktop/src/events.rs

@@ -4,7 +4,8 @@
 use std::sync::Arc;
 use std::sync::Arc;
 use std::{any::Any, rc::Rc};
 use std::{any::Any, rc::Rc};
 
 
-use dioxus_core::{events::on::MouseEvent, ElementId, EventPriority, ScopeId, UserEvent};
+use dioxus_core::{ElementId, EventPriority, ScopeId, UserEvent};
+use dioxus_html::on::*;
 
 
 #[derive(serde::Serialize, serde::Deserialize)]
 #[derive(serde::Serialize, serde::Deserialize)]
 struct ImEvent {
 struct ImEvent {
@@ -23,7 +24,7 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
         contents,
         contents,
     } = ims.into_iter().next().unwrap();
     } = ims.into_iter().next().unwrap();
 
 
-    let scope = ScopeId(scope as usize);
+    let scope_id = ScopeId(scope as usize);
     let mounted_dom_id = Some(ElementId(mounted_dom_id as usize));
     let mounted_dom_id = Some(ElementId(mounted_dom_id as usize));
 
 
     let name = event_name_from_typ(&event);
     let name = event_name_from_typ(&event);
@@ -31,15 +32,14 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
 
 
     UserEvent {
     UserEvent {
         name,
         name,
-        event,
-        scope,
+        priority: EventPriority::Low,
+        scope_id,
         mounted_dom_id,
         mounted_dom_id,
+        event,
     }
     }
 }
 }
 
 
 fn make_synthetic_event(name: &str, val: serde_json::Value) -> Box<dyn Any + Send> {
 fn make_synthetic_event(name: &str, val: serde_json::Value) -> Box<dyn Any + Send> {
-    use dioxus_core::events::on::*;
-
     match name {
     match name {
         "copy" | "cut" | "paste" => {
         "copy" | "cut" | "paste" => {
             //
             //

+ 0 - 1
packages/desktop/src/lib.rs

@@ -13,7 +13,6 @@ use std::sync::mpsc::channel;
 use std::sync::{Arc, RwLock};
 use std::sync::{Arc, RwLock};
 
 
 use cfg::DesktopConfig;
 use cfg::DesktopConfig;
-use dioxus_core::scheduler::SchedulerMsg;
 use dioxus_core::*;
 use dioxus_core::*;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 
 

+ 9 - 8
packages/hooks/src/use_shared_state.rs

@@ -82,13 +82,6 @@ pub fn use_shared_state<'a, T: 'static>(cx: Context<'a>) -> Option<UseSharedStat
                 _ => None,
                 _ => None,
             }
             }
         },
         },
-        |f| {
-            // we need to unsubscribe when our component is unounted
-            if let Some(root) = &f.root {
-                let mut root = root.borrow_mut();
-                root.consumers.remove(&f.scope_id);
-            }
-        },
     )
     )
 }
 }
 
 
@@ -98,6 +91,15 @@ struct SharedStateInner<T: 'static> {
     scope_id: ScopeId,
     scope_id: ScopeId,
     needs_notification: Cell<bool>,
     needs_notification: Cell<bool>,
 }
 }
+impl<T> Drop for SharedStateInner<T> {
+    fn drop(&mut self) {
+        // we need to unsubscribe when our component is unounted
+        if let Some(root) = &self.root {
+            let mut root = root.borrow_mut();
+            root.consumers.remove(&self.scope_id);
+        }
+    }
+}
 
 
 pub struct UseSharedState<'a, T: 'static> {
 pub struct UseSharedState<'a, T: 'static> {
     pub(crate) cx: Context<'a>,
     pub(crate) cx: Context<'a>,
@@ -172,6 +174,5 @@ pub fn use_provide_state<'a, T: 'static>(cx: Context<'a>, f: impl FnOnce() -> T)
             cx.provide_state(state)
             cx.provide_state(state)
         },
         },
         |inner| {},
         |inner| {},
-        |_| {},
     )
     )
 }
 }

+ 0 - 1
packages/hooks/src/useref.rs

@@ -16,7 +16,6 @@ pub fn use_ref<T: 'static>(cx: Context, f: impl FnOnce() -> T) -> UseRef<T> {
             inner.update_scheuled.set(false);
             inner.update_scheuled.set(false);
             UseRef { inner }
             UseRef { inner }
         },
         },
-        |_| {},
     )
     )
 }
 }
 
 

+ 0 - 1
packages/hooks/src/usestate.rs

@@ -69,7 +69,6 @@ pub fn use_state<'a, T: 'static>(
 
 
             UseState { inner: &*hook }
             UseState { inner: &*hook }
         },
         },
-        |_| {},
     )
     )
 }
 }
 struct UseStateInner<T: 'static> {
 struct UseStateInner<T: 'static> {

+ 8 - 3
packages/html/Cargo.toml

@@ -9,7 +9,12 @@ description = "HTML Element pack for Dioxus - a concurrent renderer-agnostic Vir
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 
 [dependencies]
 [dependencies]
-dioxus-core = { path = "../core", version = "0.1.2" }
+dioxus-core = { path = "../core", version = "0.1.3" }
+# Serialize the Edits for use in Webview/Liveview instances
+serde = { version = "1", features = ["derive"], optional = true }
+serde_repr = { version = "0.1.7", optional = true }
 
 
-[dev-dependencies]
-scraper = "0.12.0"
+
+[features]
+default = []
+serialize = ["serde", "serde_repr"]

+ 0 - 33
packages/html/src/attrval.rs

@@ -1,33 +0,0 @@
-//! This module is not included anywhere.
-//!
-//! It is a prototype for a system that supports non-string attribute values.
-
-trait AsAttributeValue: Sized {
-    fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a>;
-}
-enum AttributeValue<'a> {
-    Int(i32),
-    Float(f32),
-    Str(&'a str),
-    Bool(bool),
-}
-impl<'b> AsAttributeValue for Arguments<'b> {
-    fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> {
-        todo!()
-    }
-}
-impl AsAttributeValue for &'static str {
-    fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> {
-        todo!()
-    }
-}
-impl AsAttributeValue for f32 {
-    fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> {
-        todo!()
-    }
-}
-impl AsAttributeValue for i32 {
-    fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> {
-        todo!()
-    }
-}

+ 1126 - 0
packages/html/src/elements.rs

@@ -0,0 +1,1126 @@
+use crate::{GlobalAttributes, SvgAttributes};
+use dioxus_core::*;
+use std::fmt::Arguments;
+
+macro_rules! builder_constructors {
+    (
+        $(
+            $(#[$attr:meta])*
+            $name:ident {
+                $(
+                    $(#[$attr_method:meta])*
+                    $fil:ident: $vil:ident,
+                )*
+            };
+         )*
+    ) => {
+        $(
+            #[allow(non_camel_case_types)]
+            $(#[$attr])*
+            pub struct $name;
+
+            impl DioxusElement for $name {
+                const TAG_NAME: &'static str = stringify!($name);
+                const NAME_SPACE: Option<&'static str> = None;
+            }
+
+            impl GlobalAttributes for $name {}
+
+            impl $name {
+                $(
+                    $(#[$attr_method])*
+                    pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+                        cx.attr(stringify!($fil), val, None, false)
+                    }
+                )*
+            }
+        )*
+    };
+
+    ( $(
+        $(#[$attr:meta])*
+        $name:ident <> $namespace:tt {
+            $($fil:ident: $vil:ident,)*
+        };
+    )* ) => {
+        $(
+            #[allow(non_camel_case_types)]
+            $(#[$attr])*
+            pub struct $name;
+
+            impl DioxusElement for $name {
+                const TAG_NAME: &'static str = stringify!($name);
+                const NAME_SPACE: Option<&'static str> = Some($namespace);
+            }
+
+            impl SvgAttributes for $name {}
+
+            impl $name {
+                $(
+                    pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+                        cx.attr(stringify!($fil), val, Some(stringify!($namespace)), false)
+                    }
+                )*
+            }
+        )*
+    };
+}
+
+// Organized in the same order as
+// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
+//
+// Does not include obsolete elements.
+//
+// This namespace represents a collection of modern HTML-5 compatiable elements.
+//
+// This list does not include obsolete, deprecated, experimental, or poorly supported elements.
+builder_constructors! {
+    // Document metadata
+
+    /// Build a
+    /// [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
+    /// element.
+    ///
+    base {
+        href: Uri,
+        target: Target,
+    };
+
+    /// Build a
+    /// [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
+    /// element.
+    head {};
+
+    /// Build a
+    /// [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
+    /// element.
+    link {
+        // as: Mime,
+        crossorigin: CrossOrigin,
+        href: Uri,
+        hreflang: LanguageTag,
+        media: String, // FIXME media query
+        rel: LinkType,
+        sizes: String, // FIXME
+        title: String, // FIXME
+        r#type: Mime,
+        integrity: String,
+    };
+
+    /// Build a
+    /// [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
+    /// element.
+    meta {
+        charset: String, // FIXME IANA standard names
+        content: String,
+        http_equiv: HTTPEquiv,
+        name: Metadata,
+    };
+
+    /// Build a
+    /// [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
+    /// element.
+    style {
+        r#type: Mime,
+        media: String, // FIXME media query
+        nonce: Nonce,
+        title: String, // FIXME
+    };
+
+    /// Build a
+    /// [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
+    /// element.
+    title { };
+
+    // Sectioning root
+
+    /// Build a
+    /// [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
+    /// element.
+    body {};
+
+    // ------------------
+    // Content sectioning
+    // ------------------
+
+    /// Build a
+    /// [`<address>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address)
+    /// element.
+    address {};
+
+    /// Build a
+    /// [`<article>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article)
+    /// element.
+    article {};
+
+    /// Build a
+    /// [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
+    /// element.
+    aside {};
+
+    /// Build a
+    /// [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)
+    /// element.
+    footer {};
+
+    /// Build a
+    /// [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)
+    /// element.
+    header {};
+
+    /// Build a
+    /// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
+    /// element.
+    ///
+    /// # About
+    /// - The HTML `<h1>` element is found within the `<body>` tag.
+    /// - Headings can range from `<h1>` to `<h6>`.
+    /// - The most important heading is `<h1>` and the least important heading is `<h6>`.
+    /// - The `<h1>` heading is the first heading in the document.
+    /// - The `<h1>` heading is usually a large bolded font.
+    ///
+    /// # Usage
+    ///
+    /// ```
+    /// html!(<h1> A header element </h1>)
+    /// rsx!(h1 { "A header element" })
+    /// LazyNodes::new(|f| f.el(h1).children([f.text("A header element")]).finish())
+    /// ```
+    h1 {};
+
+
+    /// Build a
+    /// [`<h2>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2)
+    /// element.
+    ///
+    /// # About
+    /// - The HTML `<h2>` element is found within the `<body>` tag.
+    /// - Headings can range from `<h1>` to `<h6>`.
+    /// - The most important heading is `<h1>` and the least important heading is `<h6>`.
+    /// - The `<h2>` heading is the second heading in the document.
+    /// - The `<h2>` heading is usually a large bolded font.
+    ///
+    /// # Usage
+    /// ```
+    /// html!(<h2> A header element </h2>)
+    /// rsx!(h2 { "A header element" })
+    /// LazyNodes::new(|f| f.el(h2).children([f.text("A header element")]).finish())
+    /// ```
+    h2 {};
+
+
+    /// Build a
+    /// [`<h3>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3)
+    /// element.
+    ///
+    /// # About
+    /// - The HTML <h1> element is found within the <body> tag.
+    /// - Headings can range from <h1> to <h6>.
+    /// - The most important heading is <h1> and the least important heading is <h6>.
+    /// - The <h1> heading is the first heading in the document.
+    /// - The <h1> heading is usually a large bolded font.
+    h3 {};
+    /// Build a
+    /// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
+    /// element.
+    h4 {};
+    /// Build a
+    /// [`<h5>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5)
+    /// element.
+    h5 {};
+    /// Build a
+    /// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
+    /// element.
+    h6 {};
+
+    /// Build a
+    /// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
+    /// element.
+    main {};
+    /// Build a
+    /// [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)
+    /// element.
+    nav {};
+    /// Build a
+    /// [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)
+    /// element.
+    section {};
+
+    // Text content
+
+    /// Build a
+    /// [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)
+    /// element.
+    blockquote {
+        cite: Uri,
+    };
+    /// Build a
+    /// [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)
+    /// element.
+    dd {};
+
+    /// Build a
+    /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
+    /// element.
+    ///
+    /// Part of the HTML namespace. Only works in HTML-compatible renderers
+    ///
+    /// ## Definition and Usage
+    /// - The <div> tag defines a division or a section in an HTML document.
+    /// - The <div> tag is used as a container for HTML elements - which is then styled with CSS or manipulated with  JavaScript.
+    /// - The <div> tag is easily styled by using the class or id attribute.
+    /// - Any sort of content can be put inside the <div> tag!
+    ///
+    /// Note: By default, browsers always place a line break before and after the <div> element.
+    ///
+    /// ## Usage
+    /// ```
+    /// html!(<div> A header element </div>)
+    /// rsx!(div { "A header element" })
+    /// LazyNodes::new(|f| f.element(div, &[], &[], &[], None))
+    /// ```
+    ///
+    /// ## References:
+    /// - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div
+    /// - https://www.w3schools.com/tags/tag_div.asp
+    div {};
+
+    /// Build a
+    /// [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)
+    /// element.
+    dl {};
+
+    /// Build a
+    /// [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)
+    /// element.
+    dt {};
+
+    /// Build a
+    /// [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)
+    /// element.
+    figcaption {};
+
+    /// Build a
+    /// [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)
+    /// element.
+    figure {};
+
+    /// Build a
+    /// [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)
+    /// element.
+    hr {};
+
+    /// Build a
+    /// [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
+    /// element.
+    li {
+        value: isize,
+    };
+
+    /// Build a
+    /// [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)
+    /// element.
+    ol {
+        reversed: Bool,
+        start: isize,
+        r#type: OrderedListType,
+    };
+
+    /// Build a
+    /// [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)
+    /// element.
+    p {};
+
+    /// Build a
+    /// [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)
+    /// element.
+    pre {};
+
+    /// Build a
+    /// [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
+    /// element.
+    ul {};
+
+
+    // Inline text semantics
+
+    /// Build a
+    /// [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
+    /// element.
+    a {
+        download: String,
+        href: Uri,
+        hreflang: LanguageTag,
+        target: Target,
+        r#type: Mime,
+        // ping: SpacedList<Uri>,
+        // rel: SpacedList<LinkType>,
+        ping: SpacedList,
+        rel: SpacedList,
+    };
+
+    /// Build a
+    /// [`<abbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr)
+    /// element.
+    abbr {};
+
+    /// Build a
+    /// [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)
+    /// element.
+    b {};
+
+    /// Build a
+    /// [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)
+    /// element.
+    bdi {};
+
+    /// Build a
+    /// [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)
+    /// element.
+    bdo {};
+
+    /// Build a
+    /// [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)
+    /// element.
+    br {};
+
+    /// Build a
+    /// [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)
+    /// element.
+    cite {};
+
+    /// Build a
+    /// [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)
+    /// element.
+    code {
+        language: String,
+    };
+
+    /// Build a
+    /// [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)
+    /// element.
+    data {
+        value: String,
+    };
+
+    /// Build a
+    /// [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)
+    /// element.
+    dfn {};
+
+    /// Build a
+    /// [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)
+    /// element.
+    em {};
+
+    /// Build a
+    /// [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)
+    /// element.
+    i {};
+
+    /// Build a
+    /// [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)
+    /// element.
+    kbd {};
+
+    /// Build a
+    /// [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)
+    /// element.
+    mark {};
+
+    /// Build a
+    /// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
+    /// element.
+    q {
+        cite: Uri,
+    };
+
+
+    /// Build a
+    /// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
+    /// element.
+    rp {};
+
+
+    /// Build a
+    /// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
+    /// element.
+    rt {};
+
+
+    /// Build a
+    /// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
+    /// element.
+    ruby {};
+
+    /// Build a
+    /// [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)
+    /// element.
+    s {};
+
+    /// Build a
+    /// [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)
+    /// element.
+    samp {};
+
+    /// Build a
+    /// [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)
+    /// element.
+    small {};
+
+    /// Build a
+    /// [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)
+    /// element.
+    span {};
+
+    /// Build a
+    /// [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)
+    /// element.
+    strong {};
+
+    /// Build a
+    /// [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)
+    /// element.
+    sub {};
+
+    /// Build a
+    /// [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)
+    /// element.
+    sup {};
+
+    /// Build a
+    /// [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)
+    /// element.
+    time {};
+
+    /// Build a
+    /// [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)
+    /// element.
+    u {};
+
+    /// Build a
+    /// [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)
+    /// element.
+    var {};
+
+    /// Build a
+    /// [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)
+    /// element.
+    wbr {};
+
+
+    // Image and multimedia
+
+    /// Build a
+    /// [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
+    /// element.
+    area {
+        alt: String,
+        coords: String, // TODO could perhaps be validated
+        download: Bool,
+        href: Uri,
+        hreflang: LanguageTag,
+        shape: AreaShape,
+        target: Target,
+        // ping: SpacedList<Uri>,
+        // rel: SpacedSet<LinkType>,
+    };
+
+    /// Build a
+    /// [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
+    /// element.
+    audio {
+        autoplay: Bool,
+        controls: Bool,
+        crossorigin: CrossOrigin,
+        muted: Bool,
+        preload: Preload,
+        src: Uri,
+        r#loop: Bool,
+    };
+
+    /// Build a
+    /// [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
+    /// element.
+    img {
+        alt: String,
+        crossorigin: CrossOrigin,
+        decoding: ImageDecoding,
+        height: usize,
+        ismap: Bool,
+        src: Uri,
+        srcset: String, // FIXME this is much more complicated
+        usemap: String, // FIXME should be a fragment starting with '#'
+        width: usize,
+        referrerpolicy: String,
+        // sizes: SpacedList<String>, // FIXME it's not really just a string
+    };
+
+    /// Build a
+    /// [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)
+    /// element.
+    map {
+        name: Id,
+    };
+
+    /// Build a
+    /// [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)
+    /// element.
+    track {
+        default: Bool,
+        kind: VideoKind,
+        label: String,
+        src: Uri,
+        srclang: LanguageTag,
+    };
+
+    /// Build a
+    /// [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
+    /// element.
+    video {
+        autoplay: Bool,
+        controls: Bool,
+        crossorigin: CrossOrigin,
+        height: usize,
+        r#loop: Bool,
+        muted: Bool,
+        preload: Preload,
+        playsinline: Bool,
+        poster: Uri,
+        src: Uri,
+        width: usize,
+    };
+
+
+    // Embedded content
+
+    /// Build a
+    /// [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)
+    /// element.
+    embed {
+        height: usize,
+        src: Uri,
+        r#type: Mime,
+        width: usize,
+    };
+
+    /// Build a
+    /// [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
+    /// element.
+    iframe {
+        allow: FeaturePolicy,
+        allowfullscreen: Bool,
+        allowpaymentrequest: Bool,
+        height: usize,
+        name: Id,
+        referrerpolicy: ReferrerPolicy,
+        src: Uri,
+        srcdoc: Uri,
+        width: usize,
+
+        marginWidth: String,
+        align: String,
+        longdesc: String,
+
+        scrolling: String,
+        marginHeight: String,
+        frameBorder: String,
+        // sandbox: SpacedSet<Sandbox>,
+    };
+
+    /// Build a
+    /// [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)
+    /// element.
+    object {
+        data: Uri,
+        form: Id,
+        height: usize,
+        name: Id,
+        r#type: Mime,
+        typemustmatch: Bool,
+        usemap: String, // TODO should be a fragment starting with '#'
+        width: usize,
+    };
+
+    /// Build a
+    /// [`<param>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param)
+    /// element.
+    param {
+        name: String,
+        value: String,
+    };
+
+    /// Build a
+    /// [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
+    /// element.
+    picture {};
+
+    /// Build a
+    /// [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)
+    /// element.
+    source {
+        src: Uri,
+        r#type: Mime,
+    };
+
+
+    // Scripting
+
+    /// Build a
+    /// [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
+    /// element.
+    canvas {
+        height: usize,
+        width: usize,
+    };
+
+    /// Build a
+    /// [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)
+    /// element.
+    noscript {};
+
+    /// Build a
+    /// [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
+    /// element.
+    script {
+        crossorigin: CrossOrigin,
+        defer: Bool,
+        integrity: Integrity,
+        nomodule: Bool,
+        nonce: Nonce,
+        src: Uri,
+        text: String,
+        r#async: Bool,
+        r#type: String, // TODO could be an enum
+    };
+
+
+    // Demarcating edits
+
+    /// Build a
+    /// [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)
+    /// element.
+    del {
+        cite: Uri,
+        datetime: Datetime,
+    };
+
+    /// Build a
+    /// [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)
+    /// element.
+    ins {
+        cite: Uri,
+        datetime: Datetime,
+    };
+
+
+    // Table content
+
+    /// Build a
+    /// [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)
+    /// element.
+    caption {};
+
+    /// Build a
+    /// [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)
+    /// element.
+    col {
+        span: usize,
+    };
+
+    /// Build a
+    /// [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
+    /// element.
+    colgroup {
+        span: usize,
+    };
+
+    /// Build a
+    /// [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)
+    /// element.
+    table {};
+
+    /// Build a
+    /// [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)
+    /// element.
+    tbody {};
+
+    /// Build a
+    /// [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
+    /// element.
+    td {
+        colspan: usize,
+        rowspan: usize,
+        // headers: SpacedSet<Id>,
+    };
+
+    /// Build a
+    /// [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)
+    /// element.
+    tfoot {};
+
+    /// Build a
+    /// [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)
+    /// element.
+    th {
+        abbr: String,
+        colspan: usize,
+        rowspan: usize,
+        scope: TableHeaderScope,
+        // headers: SpacedSet<Id>,
+    };
+
+    /// Build a
+    /// [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)
+    /// element.
+    thead {};
+
+    /// Build a
+    /// [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)
+    /// element.
+    tr {};
+
+
+    // Forms
+
+    /// Build a
+    /// [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
+    /// element.
+    button {
+        autofocus: Bool,
+        disabled: Bool,
+        form: Id,
+        formaction: Uri,
+        formenctype: FormEncodingType,
+        formmethod: FormMethod,
+        formnovalidate: Bool,
+        formtarget: Target,
+        name: Id,
+        r#type: ButtonType,
+        value: String,
+    };
+
+    /// Build a
+    /// [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)
+    /// element.
+    datalist {};
+
+    /// Build a
+    /// [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)
+    /// element.
+    fieldset {};
+
+    /// Build a
+    /// [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
+    /// element.
+    form {
+        // accept-charset: SpacedList<CharacterEncoding>,
+        action: Uri,
+        autocomplete: OnOff,
+        enctype: FormEncodingType,
+        method: FormMethod,
+        name: Id,
+        novalidate: Bool,
+        target: Target,
+    };
+
+    /// Build a
+    /// [`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
+    /// element.
+    input {
+        accept: String,
+        alt: String,
+        autocomplete: String,
+        autofocus: Bool,
+        capture: String,
+        checked: Bool,
+        disabled: Bool,
+        form: Id,
+        formaction: Uri,
+        formenctype: FormEncodingType,
+        formmethod: FormDialogMethod,
+        formnovalidate: Bool,
+        formtarget: Target,
+        height: isize,
+        list: Id,
+        max: String,
+        maxlength: usize,
+        min: String,
+        minlength: usize,
+        multiple: Bool,
+        name: Id,
+        pattern: String,
+        placeholder: String,
+        readonly: Bool,
+        required: Bool,
+        size: usize,
+        spellcheck: Bool,
+        src: Uri,
+        step: String,
+        tabindex: usize,
+
+        width: isize,
+
+        // Manual implementations below...
+        // r#type: InputType,
+        // value: String,
+    };
+
+    /// Build a
+    /// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
+    /// element.
+    label {
+        form: Id,
+        // r#for: Id,
+    };
+
+    /// Build a
+    /// [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)
+    /// element.
+    legend {};
+
+    /// Build a
+    /// [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)
+    /// element.
+    meter {
+        value: isize,
+        min: isize,
+        max: isize,
+        low: isize,
+        high: isize,
+        optimum: isize,
+        form: Id,
+    };
+
+    /// Build a
+    /// [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)
+    /// element.
+    optgroup {
+        disabled: Bool,
+        label: String,
+    };
+
+    /// Build a
+    /// [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
+    /// element.
+    option {
+        disabled: Bool,
+        label: String,
+
+
+        value: String,
+
+        // defined below
+        // selected: Bool,
+    };
+
+    /// Build a
+    /// [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)
+    /// element.
+    output {
+        form: Id,
+        name: Id,
+        // r#for: SpacedSet<Id>,
+    };
+
+    /// Build a
+    /// [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
+    /// element.
+    progress {
+        max: f64,
+        value: f64,
+    };
+
+    /// Build a
+    /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
+    /// element.
+    select {
+        // defined below
+        // value: String,
+        autocomplete: String,
+        autofocus: Bool,
+        disabled: Bool,
+        form: Id,
+        multiple: Bool,
+        name: Id,
+        required: Bool,
+        size: usize,
+    };
+
+    /// Build a
+    /// [`<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
+    /// element.
+    textarea {
+        autocomplete: OnOff,
+        autofocus: Bool,
+        cols: usize,
+        disabled: Bool,
+        form: Id,
+        maxlength: usize,
+        minlength: usize,
+        name: Id,
+        placeholder: String,
+        readonly: Bool,
+        required: Bool,
+        rows: usize,
+        spellcheck: BoolOrDefault,
+        wrap: Wrap,
+    };
+
+
+    // Interactive elements
+
+    /// Build a
+    /// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
+    /// element.
+    details {
+        open: Bool,
+    };
+
+
+
+    /// Build a
+    /// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
+    /// element.
+    summary {};
+
+    // Web components
+
+    /// Build a
+    /// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
+    /// element.
+    slot {};
+
+    /// Build a
+    /// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
+    /// element.
+    template {};
+}
+
+impl input {
+    /// The type of input
+    ///
+    /// Here are the different input types you can use in HTML:
+    ///
+    /// - `button`
+    /// - `checkbox`
+    /// - `color`
+    /// - `date`
+    /// - `datetime-local`
+    /// - `email`
+    /// - `file`
+    /// - `hidden`
+    /// - `image`
+    /// - `month`
+    /// - `number`
+    /// - `password`
+    /// - `radio`
+    /// - `range`
+    /// - `reset`
+    /// - `search`
+    /// - `submit`
+    /// - `tel`
+    /// - `text`
+    /// - `time`
+    /// - `url`
+    /// - `week`    
+    pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("type", val, None, false)
+    }
+
+    pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("value", val, None, true)
+    }
+}
+
+/*
+volatile attributes
+*/
+
+impl select {
+    pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("value", val, None, true)
+    }
+}
+
+impl option {
+    pub fn selected<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("selected", val, None, true)
+    }
+}
+
+impl textarea {
+    pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("value", val, None, true)
+    }
+}
+impl label {
+    pub fn r#for<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("for", val, None, false)
+    }
+}
+
+builder_constructors! {
+    // SVG components
+    /// Build a
+    /// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
+    /// element.
+    svg <> "http://www.w3.org/2000/svg" { };
+
+    /// Build a
+    /// [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)
+    /// element.
+    path <> "http://www.w3.org/2000/svg" {
+
+    };
+
+    /// Build a
+    /// [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)
+    /// element.
+    circle <>  "http://www.w3.org/2000/svg" {
+
+    };
+
+    /// Build a
+    /// [`<ellipse>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse)
+    /// element.
+    ellipse <> "http://www.w3.org/2000/svg" {
+
+    };
+
+    /// Build a
+    /// [`<line>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line)
+    /// element.
+    line <> "http://www.w3.org/2000/svg" {
+
+    };
+
+    /// Build a
+    /// [`<polygon>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon)
+    /// element.
+    polygon <> "http://www.w3.org/2000/svg" {
+
+    };
+
+    /// Build a
+    /// [`<polyline>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline)
+    /// element.
+    polyline <> "http://www.w3.org/2000/svg" {
+
+    };
+
+    /// Build a
+    /// [`<rect>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect)
+    /// element.
+    rect <> "http://www.w3.org/2000/svg" {
+
+    };
+
+    /// Build a
+    /// [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image)
+    /// element.
+    image <> "http://www.w3.org/2000/svg" {
+
+    };
+
+}

+ 5 - 91
packages/core/src/events.rs → packages/html/src/events.rs

@@ -1,89 +1,7 @@
-//! An event system that's less confusing than Traits + RC;
-//! This should hopefully make it easier to port to other platforms.
-//!
-//! Unfortunately, it is less efficient than the original, but hopefully it's negligible.
-
-use crate::{
-    innerlude::Listener,
-    innerlude::{ElementId, NodeFactory, ScopeId},
-};
 use bumpalo::boxed::Box as BumpBox;
 use bumpalo::boxed::Box as BumpBox;
-use std::{
-    any::Any,
-    cell::{Cell, RefCell},
-    fmt::Debug,
-};
-
-pub use on::*;
-
-#[derive(Debug)]
-pub struct UserEvent {
-    /// The originator of the event trigger
-    pub scope: ScopeId,
-
-    /// The optional real node associated with the trigger
-    pub mounted_dom_id: Option<ElementId>,
-
-    /// The event type IE "onclick" or "onmouseover"
-    ///
-    /// The name that the renderer will use to mount the listener.
-    pub name: &'static str,
-
-    /// The type of event
-    pub event: Box<dyn Any + Send>,
-}
-
-/// Priority of Event Triggers.
-///
-/// Internally, Dioxus will abort work that's taking too long if new, more important work arrives. Unlike React, Dioxus
-/// won't be afraid to pause work or flush changes to the RealDOM. This is called "cooperative scheduling". Some Renderers
-/// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
-///
-/// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes.
-///
-/// React has a 5-tier priority system. However, they break things into "Continuous" and "Discrete" priority. For now,
-/// we keep it simple, and just use a 3-tier priority system.
-///
-/// - NoPriority = 0
-/// - LowPriority = 1
-/// - NormalPriority = 2
-/// - UserBlocking = 3
-/// - HighPriority = 4
-/// - ImmediatePriority = 5
-///
-/// We still have a concept of discrete vs continuous though - discrete events won't be batched, but continuous events will.
-/// This means that multiple "scroll" events will be processed in a single frame, but multiple "click" events will be
-/// flushed before proceeding. Multiple discrete events is highly unlikely, though.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
-pub enum EventPriority {
-    /// Work that must be completed during the EventHandler phase.
-    ///
-    /// Currently this is reserved for controlled inputs.
-    Immediate = 3,
-
-    /// "High Priority" work will not interrupt other high priority work, but will interrupt medium and low priority work.
-    ///
-    /// This is typically reserved for things like user interaction.
-    ///
-    /// React calls these "discrete" events, but with an extra category of "user-blocking" (Immediate).
-    High = 2,
-
-    /// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
-    /// than "High Priority" events and will take precedence over low priority events.
-    ///
-    /// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
-    ///
-    /// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc).
-    Medium = 1,
-
-    /// "Low Priority" work will always be preempted unless the work is significantly delayed, in which case it will be
-    /// advanced to the front of the work queue until completed.
-    ///
-    /// The primary user of Low Priority work is the asynchronous work system (Suspense).
-    ///
-    /// This is considered "idle" work or "background" work.
-    Low = 0,
-}
+use dioxus_core::exports::bumpalo;
+use dioxus_core::*;
+use std::any::Any;
 
 
 pub mod on {
 pub mod on {
     use super::*;
     use super::*;
@@ -125,11 +43,7 @@ pub mod on {
                         // ie copy
                         // ie copy
                         let shortname: &'static str = &event_name[2..];
                         let shortname: &'static str = &event_name[2..];
 
 
-                        Listener {
-                            event: shortname,
-                            mounted_node: Cell::new(None),
-                            callback: RefCell::new(Some(callback)),
-                        }
+                        c.listener(shortname, callback)
                     }
                     }
                 )*
                 )*
             )*
             )*
@@ -1148,7 +1062,7 @@ impl KeyCode {
     }
     }
 }
 }
 
 
-pub(crate) fn event_meta(event: &UserEvent) -> (bool, EventPriority) {
+pub(crate) fn _event_meta(event: &UserEvent) -> (bool, EventPriority) {
     use EventPriority::*;
     use EventPriority::*;
 
 
     match event.name {
     match event.name {

+ 925 - 0
packages/html/src/global_attributes.rs

@@ -0,0 +1,925 @@
+use dioxus_core::*;
+use std::fmt::Arguments;
+
+macro_rules! no_namespace_trait_methods {
+    (
+        $(
+            $(#[$attr:meta])*
+            $name:ident;
+        )*
+    ) => {
+        $(
+            $(#[$attr])*
+            fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+                cx.attr(stringify!($name), val, None, false)
+            }
+        )*
+    };
+}
+macro_rules! style_trait_methods {
+    (
+        $(
+            $(#[$attr:meta])*
+            $name:ident: $lit:literal,
+        )*
+    ) => {
+        $(
+            #[inline]
+            $(#[$attr])*
+            fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+                cx.attr($lit, val, Some("style"), false)
+            }
+        )*
+    };
+}
+macro_rules! aria_trait_methods {
+    (
+        $(
+            $(#[$attr:meta])*
+            $name:ident: $lit:literal,
+        )*
+    ) => {
+        $(
+            $(#[$attr])*
+            fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+                cx.attr($lit, val, None, false)
+            }
+        )*
+    };
+}
+
+pub trait GlobalAttributes {
+    no_namespace_trait_methods! {
+        accesskey;
+
+        /// The HTML class attribute is used to specify a class for an HTML element.
+        ///
+        /// ## Details
+        /// Multiple HTML elements can share the same class.
+        ///
+        /// The class global attribute is a space-separated list of the case-sensitive classes of the element.
+        /// Classes allow CSS and Javascript to select and access specific elements via the class selectors or
+        /// functions like the DOM method document.getElementsByClassName.
+        ///
+        /// ## Example
+        ///
+        /// ### HTML:
+        /// ```html
+        /// <p class="note editorial">Above point sounds a bit obvious. Remove/rewrite?</p>
+        /// ```
+        ///
+        /// ### CSS:
+        /// ```css
+        /// .note {
+        ///     font-style: italic;
+        ///     font-weight: bold;
+        /// }
+        ///
+        /// .editorial {
+        ///     background: rgb(255, 0, 0, .25);
+        ///     padding: 10px;
+        /// }
+        /// ```
+        class;
+        contenteditable;
+        data;
+        dir;
+        draggable;
+        hidden;
+        id;
+        lang;
+        spellcheck;
+        style;
+        tabindex;
+        title;
+        translate;
+
+        /// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting
+        /// HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS)
+        /// attack. So, you can set HTML directly from Dioxus, but you have to type out dangerous_inner_html to remind
+        /// yourself that it’s dangerous
+        dangerous_inner_html;
+    }
+
+    // This macro creates an explicit method call for each of the style attributes.
+    //
+    // The left token specifies the name of the attribute in the rsx! macro, and the right string literal specifies the
+    // actual name of the attribute generated.
+    //
+    // This roughly follows the html spec
+    style_trait_methods! {
+        /// Specifies the alignment of flexible container's items within the flex container.
+        align_content: "align-content",
+
+        /// Specifies the default alignment for items within the flex container.
+        align_items: "align-items",
+
+        /// Specifies the alignment for selected items within the flex container.
+        align_self: "align-self",
+
+        /// Specifies the keyframe_based animations.
+        animation: "animation",
+
+        /// Specifies when the animation will start.
+        animation_delay: "animation-delay",
+
+        /// Specifies whether the animation should play in reverse on alternate cycles or not.
+        animation_direction: "animation-direction",
+
+        /// Specifies the number of seconds or milliseconds an animation should take to complete one cycle
+        animation_duration: "animation-duration",
+
+        /// Specifies how a CSS animation should apply styles to its target before and after it is executing
+        animation_fill_mode: "animation-fill-mode",
+
+        /// Specifies the number of times an animation cycle should be played before stopping.
+        animation_iteration_count: "animation-iteration-count",
+
+        /// Specifies the name of @keyframes defined animations that should be applied to the selected element
+        animation_name: "animation-name",
+
+        /// Specifies whether the animation is running or paused.
+        animation_play_state: "animation-play-state",
+
+        /// Specifies how a CSS animation should progress over the duration of each cycle.
+        animation_timing_function: "animation-timing-function",
+
+        /// Specifies whether or not the "back" side of a transformed element is visible when facing the user.
+        backface_visibility: "backface-visibility",
+
+        /// Defines a variety of background properties within one declaration.
+        background: "background",
+
+        /// Specify whether the background image is fixed in the viewport or scrolls.
+        background_attachment: "background-attachment",
+
+        /// Specifies the painting area of the background.
+        background_clip: "background-clip",
+
+        /// Defines an element's background color.
+        background_color: "background-color",
+
+        /// Defines an element's background image.
+        background_image: "background-image",
+
+        /// Specifies the positioning area of the background images.
+        background_origin: "background-origin",
+
+        /// Defines the origin of a background image.
+        background_position: "background-position",
+
+        /// Specify whether/how the background image is tiled.
+        background_repeat: "background-repeat",
+
+        /// Specifies the size of the background images.
+        background_size: "background-size",
+
+        /// Sets the width, style, and color for all four sides of an element's border.
+        border: "border",
+
+        /// Sets the width, style, and color of the bottom border of an element.
+        border_bottom: "border-bottom",
+
+        /// Sets the color of the bottom border of an element.
+        border_bottom_color: "border-bottom-color",
+
+        /// Defines the shape of the bottom_left border corner of an element.
+        border_bottom_left_radius: "border-bottom-left-radius",
+
+        /// Defines the shape of the bottom_right border corner of an element.
+        border_bottom_right_radius: "border-bottom-right-radius",
+
+        /// Sets the style of the bottom border of an element.
+        border_bottom_style: "border-bottom-style",
+
+        /// Sets the width of the bottom border of an element.
+        border_bottom_width: "border-bottom-width",
+
+        /// Specifies whether table cell borders are connected or separated.
+        border_collapse: "border-collapse",
+
+        /// Sets the color of the border on all the four sides of an element.
+        border_color: "border-color",
+
+        /// Specifies how an image is to be used in place of the border styles.
+        border_image: "border-image",
+
+        /// Specifies the amount by which the border image area extends beyond the border box.
+        border_image_outset: "border-image-outset",
+
+        /// Specifies whether the image_border should be repeated, rounded or stretched.
+        border_image_repeat: "border-image-repeat",
+
+        /// Specifies the inward offsets of the image_border.
+        border_image_slice: "border-image-slice",
+
+        /// Specifies the location of the image to be used as a border.
+        border_image_source: "border-image-source",
+
+        /// Specifies the width of the image_border.
+        border_image_width: "border-image-width",
+
+        /// Sets the width, style, and color of the left border of an element.
+        border_left: "border-left",
+
+        /// Sets the color of the left border of an element.
+        border_left_color: "border-left-color",
+
+        /// Sets the style of the left border of an element.
+        border_left_style: "border-left-style",
+
+        /// Sets the width of the left border of an element.
+        border_left_width: "border-left-width",
+
+        /// Defines the shape of the border corners of an element.
+        border_radius: "border-radius",
+
+        /// Sets the width, style, and color of the right border of an element.
+        border_right: "border-right",
+
+        /// Sets the color of the right border of an element.
+        border_right_color: "border-right-color",
+
+        /// Sets the style of the right border of an element.
+        border_right_style: "border-right-style",
+
+        /// Sets the width of the right border of an element.
+        border_right_width: "border-right-width",
+
+        /// Sets the spacing between the borders of adjacent table cells.
+        border_spacing: "border-spacing",
+
+        /// Sets the style of the border on all the four sides of an element.
+        border_style: "border-style",
+
+        /// Sets the width, style, and color of the top border of an element.
+        border_top: "border-top",
+
+        /// Sets the color of the top border of an element.
+        border_top_color: "border-top-color",
+
+        /// Defines the shape of the top_left border corner of an element.
+        border_top_left_radius: "border-top-left-radius",
+
+        /// Defines the shape of the top_right border corner of an element.
+        border_top_right_radius: "border-top-right-radius",
+
+        /// Sets the style of the top border of an element.
+        border_top_style: "border-top-style",
+
+        /// Sets the width of the top border of an element.
+        border_top_width: "border-top-width",
+
+        /// Sets the width of the border on all the four sides of an element.
+        border_width: "border-width",
+
+        /// Specify the location of the bottom edge of the positioned element.
+        bottom: "bottom",
+
+        /// Applies one or more drop_shadows to the element's box.
+        box_shadow: "box-shadow",
+
+        /// Alter the default CSS box model.
+        box_sizing: "box-sizing",
+
+        /// Specify the position of table's caption.
+        caption_side: "caption-side",
+
+        /// Specifies the placement of an element in relation to floating elements.
+        clear: "clear",
+
+        /// Defines the clipping region.
+        clip: "clip",
+
+        /// Specify the color of the text of an element.
+        color: "color",
+
+        /// Specifies the number of columns in a multi_column element.
+        column_count: "column-count",
+
+        /// Specifies how columns will be filled.
+        column_fill: "column-fill",
+
+        /// Specifies the gap between the columns in a multi_column element.
+        column_gap: "column-gap",
+
+        /// Specifies a straight line, or "rule", to be drawn between each column in a multi_column element.
+        column_rule: "column-rule",
+
+        /// Specifies the color of the rules drawn between columns in a multi_column layout.
+        column_rule_color: "column-rule-color",
+
+        /// Specifies the style of the rule drawn between the columns in a multi_column layout.
+        column_rule_style: "column-rule-style",
+
+        /// Specifies the width of the rule drawn between the columns in a multi_column layout.
+        column_rule_width: "column-rule-width",
+
+        /// Specifies how many columns an element spans across in a multi_column layout.
+        column_span: "column-span",
+
+        /// Specifies the optimal width of the columns in a multi_column element.
+        column_width: "column-width",
+
+        /// A shorthand property for setting column_width and column_count properties.
+        columns: "columns",
+
+        /// Inserts generated content.
+        content: "content",
+
+        /// Increments one or more counter values.
+        counter_increment: "counter-increment",
+
+        /// Creates or resets one or more counters.
+        counter_reset: "counter-reset",
+
+        /// Specify the type of cursor.
+        cursor: "cursor",
+
+        /// Define the text direction/writing direction.
+        direction: "direction",
+
+        /// Specifies how an element is displayed onscreen.
+        display: "display",
+
+        /// Show or hide borders and backgrounds of empty table cells.
+        empty_cells: "empty-cells",
+
+        /// Specifies the components of a flexible length.
+        flex: "flex",
+
+        /// Specifies the initial main size of the flex item.
+        flex_basis: "flex-basis",
+
+        /// Specifies the direction of the flexible items.
+        flex_direction: "flex-direction",
+
+        /// A shorthand property for the flex_direction and the flex_wrap properties.
+        flex_flow: "flex-flow",
+
+        /// Specifies how the flex item will grow relative to the other items inside the flex container.
+        flex_grow: "flex-grow",
+
+        /// Specifies how the flex item will shrink relative to the other items inside the flex container
+        flex_shrink: "flex-shrink",
+
+        /// Specifies whether the flexible items should wrap or not.
+        flex_wrap: "flex-wrap",
+
+        /// Specifies whether or not a box should float.
+        float: "float",
+
+        /// Defines a variety of font properties within one declaration.
+        font: "font",
+
+        /// Defines a list of fonts for element.
+        font_family: "font-family",
+
+        /// Defines the font size for the text.
+        font_size: "font-size",
+
+        /// Preserves the readability of text when font fallback occurs.
+        font_size_adjust: "font-size-adjust",
+
+        /// Selects a normal, condensed, or expanded face from a font.
+        font_stretch: "font-stretch",
+
+        /// Defines the font style for the text.
+        font_style: "font-style",
+
+        /// Specify the font variant.
+        font_variant: "font-variant",
+
+        /// Specify the font weight of the text.
+        font_weight: "font-weight",
+
+        /// Specify the height of an element.
+        height: "height",
+
+        /// Specifies how flex items are aligned along the main axis of the flex container after any flexible lengths and auto margins have been resolved.
+        justify_content: "auto margins have been resolved.",
+
+        /// Specify the location of the left edge of the positioned element.
+        left: "left",
+
+        /// Sets the extra spacing between letters.
+        letter_spacing: "letter-spacing",
+
+        /// Sets the height between lines of text.
+        line_height: "line-height",
+
+        /// Defines the display style for a list and list elements.
+        list_style: "list-style",
+
+        /// Specifies the image to be used as a list_item marker.
+        list_style_image: "list-style-image",
+
+        /// Specifies the position of the list_item marker.
+        list_style_position: "list-style-position",
+
+        /// Specifies the marker style for a list_item.
+        list_styler_type: "list-style-type",
+
+        /// Sets the margin on all four sides of the element.
+        margin: "margin",
+
+        /// Sets the bottom margin of the element.
+        margin_bottom: "margin-bottom",
+
+        /// Sets the left margin of the element.
+        margin_left: "margin-left",
+
+        /// Sets the right margin of the element.
+        margin_right: "margin-right",
+
+        /// Sets the top margin of the element.
+        margin_top: "margin-top",
+
+        /// Specify the maximum height of an element.
+        max_height: "max-height",
+
+        /// Specify the maximum width of an element.
+        max_width: "max-width",
+
+        /// Specify the minimum height of an element.
+        min_height: "min-height",
+
+        /// Specify the minimum width of an element.
+        min_width: "min-width",
+
+        /// Specifies the transparency of an element.
+        opacity: "opacity",
+
+        /// Specifies the order in which a flex items are displayed and laid out within a flex container.
+        order: "order",
+
+        /// Sets the width, style, and color for all four sides of an element's outline.
+        outline: "outline",
+
+        /// Sets the color of the outline.
+        outline_color: "outline-color",
+
+        /// Set the space between an outline and the border edge of an element.
+        outline_offset: "outline-offset",
+
+        /// Sets a style for an outline.
+        outline_style: "outline-style",
+
+        /// Sets the width of the outline.
+        outline_width: "outline-width",
+
+        /// Specifies the treatment of content that overflows the element's box.
+        overflow: "overflow",
+
+        /// Specifies the treatment of content that overflows the element's box horizontally.
+        overflow_x: "overflow-x",
+
+        /// Specifies the treatment of content that overflows the element's box vertically.
+        overflow_y: "overflow-y",
+
+        /// Sets the padding on all four sides of the element.
+        padding: "padding",
+
+        /// Sets the padding to the bottom side of an element.
+        padding_bottom: "padding-bottom",
+
+        /// Sets the padding to the left side of an element.
+        padding_left: "padding-left",
+
+        /// Sets the padding to the right side of an element.
+        padding_right: "padding-right",
+
+        /// Sets the padding to the top side of an element.
+        padding_top: "padding-top",
+
+        /// Insert a page breaks after an element.
+        page_break_after: "page-break-after",
+
+        /// Insert a page breaks before an element.
+        page_break_before: "page-break-before",
+
+        /// Insert a page breaks inside an element.
+        page_break_inside: "page-break-inside",
+
+        /// Defines the perspective from which all child elements of the object are viewed.
+        perspective: "perspective",
+
+        /// Defines the origin (the vanishing point for the 3D space) for the perspective property.
+        perspective_origin: "perspective-origin",
+
+        /// Specifies how an element is positioned.
+        position: "position",
+
+        /// The pointer-events CSS property sets under what circumstances (if any) a particular graphic element can
+        /// become the target of pointer events.
+        ///
+        /// MDN: [`pointer_events`](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events)
+        pointer_events: "pointer-events",
+
+        /// Specifies quotation marks for embedded quotations.
+        quotes: "quotes",
+
+        /// Specifies whether or not an element is resizable by the user.
+        resize: "resize",
+
+        /// Specify the location of the right edge of the positioned element.
+        right: "right",
+
+        /// Specifies the length of the tab character.
+        tab_size: "tab-size",
+
+        /// Specifies a table layout algorithm.
+        table_layout: "table-layout",
+
+        /// Sets the horizontal alignment of inline content.
+        text_align: "text-align",
+        /// Specifies how the last line of a block or a line right before a forced line break is aligned when  is justify.",
+        text_align_last: "text-align-last",
+
+        /// Specifies the decoration added to text.
+        text_decoration: "text-decoration",
+
+        /// Specifies the color of the text_decoration_line.
+        text_decoration_color: "text-decoration-color",
+
+        /// Specifies what kind of line decorations are added to the element.
+        text_decoration_line: "text-decoration-line",
+
+        /// Specifies the style of the lines specified by the text_decoration_line property
+        text_decoration_style: "text-decoration-style",
+
+        /// Indent the first line of text.
+        text_indent: "text-indent",
+
+        /// Specifies the justification method to use when the text_align property is set to justify.
+        text_justify: "text-justify",
+
+        /// Specifies how the text content will be displayed, when it overflows the block containers.
+        text_overflow: "text-overflow",
+
+        /// Applies one or more shadows to the text content of an element.
+        text_shadow: "text-shadow",
+
+        /// Transforms the case of the text.
+        text_transform: "text-transform",
+
+        /// Specify the location of the top edge of the positioned element.
+        top: "top",
+
+        /// Applies a 2D or 3D transformation to an element.
+        transform: "transform",
+
+        /// Defines the origin of transformation for an element.
+        transform_origin: "transform-origin",
+
+        /// Specifies how nested elements are rendered in 3D space.
+        transform_style: "transform-style",
+
+        /// Defines the transition between two states of an element.
+        transition: "transition",
+
+        /// Specifies when the transition effect will start.
+        transition_delay: "transition-delay",
+
+        /// Specifies the number of seconds or milliseconds a transition effect should take to complete.
+        transition_duration: "transition-duration",
+
+        /// Specifies the names of the CSS properties to which a transition effect should be applied.
+        transition_property: "transition-property",
+
+        /// Specifies the speed curve of the transition effect.
+        transition_timing_function: "transition-timing-function",
+
+        /// Sets the vertical positioning of an element relative to the current text baseline.
+        vertical_align: "vertical-align",
+
+        /// Specifies whether or not an element is visible.
+        visibility: "visibility",
+
+        /// Specifies how white space inside the element is handled.
+        white_space: "white-space",
+
+        /// Specify the width of an element.
+        width: "width",
+
+        /// Specifies how to break lines within words.
+        word_break: "word-break",
+
+        /// Sets the spacing between words.
+        word_spacing: "word-spacing",
+
+        /// Specifies whether to break words when the content overflows the boundaries of its container.
+        word_wrap: "word-wrap",
+
+        /// Specifies a layering or stacking order for positioned elements.
+        z_index	: "z-index	",
+
+    }
+    aria_trait_methods! {
+        aria_current: "aria-current",
+        aria_details: "aria-details",
+        aria_disabled: "aria-disabled",
+        aria_hidden: "aria-hidden",
+        aria_invalid: "aria-invalid",
+        aria_keyshortcuts: "aria-keyshortcuts",
+        aria_label: "aria-label",
+        aria_roledescription: "aria-roledescription",
+        // Widget Attributes
+        aria_autocomplete: "aria-autocomplete",
+        aria_checked: "aria-checked",
+        aria_expanded: "aria-expanded",
+        aria_haspopup: "aria-haspopup",
+        aria_level: "aria-level",
+        aria_modal: "aria-modal",
+        aria_multiline: "aria-multiline",
+        aria_multiselectable: "aria-multiselectable",
+        aria_orientation: "aria-orientation",
+        aria_placeholder: "aria-placeholder",
+        aria_pressed: "aria-pressed",
+        aria_readonly: "aria-readonly",
+        aria_required: "aria-required",
+        aria_selected: "aria-selected",
+        aria_sort: "aria-sort",
+        aria_valuemax: "aria-valuemax",
+        aria_valuemin: "aria-valuemin",
+        aria_valuenow: "aria-valuenow",
+        aria_valuetext: "aria-valuetext",
+        // Live Region Attributes
+        aria_atomic: "aria-atomic",
+        aria_busy: "aria-busy",
+        aria_live: "aria-live",
+        aria_relevant: "aria-relevant",
+
+        aria_dropeffect: "aria-dropeffect",
+        aria_grabbed: "aria-grabbed",
+        // Relationship Attributes
+        aria_activedescendant: "aria-activedescendant",
+        aria_colcount: "aria-colcount",
+        aria_colindex: "aria-colindex",
+        aria_colspan: "aria-colspan",
+        aria_controls: "aria-controls",
+        aria_describedby: "aria-describedby",
+        aria_errormessage: "aria-errormessage",
+        aria_flowto: "aria-flowto",
+        aria_labelledby: "aria-labelledby",
+        aria_owns: "aria-owns",
+        aria_posinset: "aria-posinset",
+        aria_rowcount: "aria-rowcount",
+        aria_rowindex: "aria-rowindex",
+        aria_rowspan: "aria-rowspan",
+        aria_setsize: "aria-setsize",
+    }
+}
+
+pub trait SvgAttributes {
+    aria_trait_methods! {
+        accent_height: "accent-height",
+        accumulate: "accumulate",
+        additive: "additive",
+        alignment_baseline: "alignment-baseline",
+        alphabetic: "alphabetic",
+        amplitude: "amplitude",
+        arabic_form: "arabic-form",
+        ascent: "ascent",
+        attributeName: "attributeName",
+        attributeType: "attributeType",
+        azimuth: "azimuth",
+        baseFrequency: "baseFrequency",
+        baseline_shift: "baseline-shift",
+        baseProfile: "baseProfile",
+        bbox: "bbox",
+        begin: "begin",
+        bias: "bias",
+        by: "by",
+        calcMode: "calcMode",
+        cap_height: "cap-height",
+        class: "class",
+        clip: "clip",
+        clipPathUnits: "clipPathUnits",
+        clip_path: "clip-path",
+        clip_rule: "clip-rule",
+        color: "color",
+        color_interpolation: "color-interpolation",
+        color_interpolation_filters: "color-interpolation-filters",
+        color_profile: "color-profile",
+        color_rendering: "color-rendering",
+        contentScriptType: "contentScriptType",
+        contentStyleType: "contentStyleType",
+        crossorigin: "crossorigin",
+        cursor: "cursor",
+        cx: "cx",
+        cy: "cy",
+        d: "d",
+        decelerate: "decelerate",
+        descent: "descent",
+        diffuseConstant: "diffuseConstant",
+        direction: "direction",
+        display: "display",
+        divisor: "divisor",
+        dominant_baseline: "dominant-baseline",
+        dur: "dur",
+        dx: "dx",
+        dy: "dy",
+        edgeMode: "edgeMode",
+        elevation: "elevation",
+        enable_background: "enable-background",
+        end: "end",
+        exponent: "exponent",
+        fill: "fill",
+        fill_opacity: "fill-opacity",
+        fill_rule: "fill-rule",
+        filter: "filter",
+        filterRes: "filterRes",
+        filterUnits: "filterUnits",
+        flood_color: "flood-color",
+        flood_opacity: "flood-opacity",
+        font_family: "font-family",
+        font_size: "font-size",
+        font_size_adjust: "font-size-adjust",
+        font_stretch: "font-stretch",
+        font_style: "font-style",
+        font_variant: "font-variant",
+        font_weight: "font-weight",
+        format: "format",
+        from: "from",
+        fr: "fr",
+        fx: "fx",
+        fy: "fy",
+        g1: "g1",
+        g2: "g2",
+        glyph_name: "glyph-name",
+        glyph_orientation_horizontal: "glyph-orientation-horizontal",
+        glyph_orientation_vertical: "glyph-orientation-vertical",
+        glyphRef: "glyphRef",
+        gradientTransform: "gradientTransform",
+        gradientUnits: "gradientUnits",
+        hanging: "hanging",
+        height: "height",
+        href: "href",
+        hreflang: "hreflang",
+        horiz_adv_x: "horiz-adv-x",
+        horiz_origin_x: "horiz-origin-x",
+        id: "id",
+        ideographic: "ideographic",
+        image_rendering: "image-rendering",
+        _in: "_in",
+        in2: "in2",
+        intercept: "intercept",
+        k: "k",
+        k1: "k1",
+        k2: "k2",
+        k3: "k3",
+        k4: "k4",
+        kernelMatrix: "kernelMatrix",
+        kernelUnitLength: "kernelUnitLength",
+        kerning: "kerning",
+        keyPoints: "keyPoints",
+        keySplines: "keySplines",
+        keyTimes: "keyTimes",
+        lang: "lang",
+        lengthAdjust: "lengthAdjust",
+        letter_spacing: "letter-spacing",
+        lighting_color: "lighting-color",
+        limitingConeAngle: "limitingConeAngle",
+        local: "local",
+        marker_end: "marker-end",
+        marker_mid: "marker-mid",
+        marker_start: "marker_start",
+        markerHeight: "markerHeight",
+        markerUnits: "markerUnits",
+        markerWidth: "markerWidth",
+        mask: "mask",
+        maskContentUnits: "maskContentUnits",
+        maskUnits: "maskUnits",
+        mathematical: "mathematical",
+        max: "max",
+        media: "media",
+        method: "method",
+        min: "min",
+        mode: "mode",
+        name: "name",
+        numOctaves: "numOctaves",
+        offset: "offset",
+        opacity: "opacity",
+        operator: "operator",
+        order: "order",
+        orient: "orient",
+        orientation: "orientation",
+        origin: "origin",
+        overflow: "overflow",
+        overline_position: "overline-position",
+        overline_thickness: "overline-thickness",
+        panose_1: "panose-1",
+        paint_order: "paint-order",
+        path: "path",
+        pathLength: "pathLength",
+        patternContentUnits: "patternContentUnits",
+        patternTransform: "patternTransform",
+        patternUnits: "patternUnits",
+        ping: "ping",
+        pointer_events: "pointer-events",
+        points: "points",
+        pointsAtX: "pointsAtX",
+        pointsAtY: "pointsAtY",
+        pointsAtZ: "pointsAtZ",
+        preserveAlpha: "preserveAlpha",
+        preserveAspectRatio: "preserveAspectRatio",
+        primitiveUnits: "primitiveUnits",
+        r: "r",
+        radius: "radius",
+        referrerPolicy: "referrerPolicy",
+        refX: "refX",
+        refY: "refY",
+        rel: "rel",
+        rendering_intent: "rendering-intent",
+        repeatCount: "repeatCount",
+        repeatDur: "repeatDur",
+        requiredExtensions: "requiredExtensions",
+        requiredFeatures: "requiredFeatures",
+        restart: "restart",
+        result: "result",
+        rotate: "rotate",
+        rx: "rx",
+        ry: "ry",
+        scale: "scale",
+        seed: "seed",
+        shape_rendering: "shape-rendering",
+        slope: "slope",
+        spacing: "spacing",
+        specularConstant: "specularConstant",
+        specularExponent: "specularExponent",
+        speed: "speed",
+        spreadMethod: "spreadMethod",
+        startOffset: "startOffset",
+        stdDeviation: "stdDeviation",
+        stemh: "stemh",
+        stemv: "stemv",
+        stitchTiles: "stitchTiles",
+        stop_color: "stop_color",
+        stop_opacity: "stop_opacity",
+        strikethrough_position: "strikethrough-position",
+        strikethrough_thickness: "strikethrough-thickness",
+        string: "string",
+        stroke: "stroke",
+        stroke_dasharray: "stroke-dasharray",
+        stroke_dashoffset: "stroke-dashoffset",
+        stroke_linecap: "stroke-linecap",
+        stroke_linejoin: "stroke-linejoin",
+        stroke_miterlimit: "stroke-miterlimit",
+        stroke_opacity: "stroke-opacity",
+        stroke_width: "stroke-width",
+        style: "style",
+        surfaceScale: "surfaceScale",
+        systemLanguage: "systemLanguage",
+        tabindex: "tabindex",
+        tableValues: "tableValues",
+        target: "target",
+        targetX: "targetX",
+        targetY: "targetY",
+        text_anchor: "text-anchor",
+        text_decoration: "text-decoration",
+        text_rendering: "text-rendering",
+        textLength: "textLength",
+        to: "to",
+        transform: "transform",
+        transform_origin: "transform-origin",
+        r#type: "_type",
+        u1: "u1",
+        u2: "u2",
+        underline_position: "underline-position",
+        underline_thickness: "underline-thickness",
+        unicode: "unicode",
+        unicode_bidi: "unicode-bidi",
+        unicode_range: "unicode-range",
+        units_per_em: "units-per-em",
+        v_alphabetic: "v-alphabetic",
+        v_hanging: "v-hanging",
+        v_ideographic: "v-ideographic",
+        v_mathematical: "v-mathematical",
+        values: "values",
+        vector_effect: "vector-effect",
+        version: "version",
+        vert_adv_y: "vert-adv-y",
+        vert_origin_x: "vert-origin-x",
+        vert_origin_y: "vert-origin-y",
+        view_box: "viewBox",
+        view_target: "viewTarget",
+        visibility: "visibility",
+        width: "width",
+        widths: "widths",
+        word_spacing: "word-spacing",
+        writing_mode: "writing-mode",
+        x: "x",
+        x_height: "x-height",
+        x1: "x1",
+        x2: "x2",
+        xmlns: "xmlns",
+        x_channel_selector: "xChannelSelector",
+        y: "y",
+        y1: "y1",
+        y2: "y2",
+        y_channel_selector: "yChannelSelector",
+        z: "z",
+        zoomAndPan: "zoomAndPan",
+    }
+}

+ 7 - 2183
packages/html/src/lib.rs

@@ -1,4 +1,5 @@
 #![allow(non_snake_case)]
 #![allow(non_snake_case)]
+
 //! # Dioxus Namespace for HTML
 //! # Dioxus Namespace for HTML
 //!
 //!
 //! This crate provides a set of compile-time correct HTML elements that can be used with the Rsx and Html macros.
 //! This crate provides a set of compile-time correct HTML elements that can be used with the Rsx and Html macros.
@@ -12,2187 +13,10 @@
 //!
 //!
 //! Currently, we don't validate for structures, but do validate attributes.
 //! Currently, we don't validate for structures, but do validate attributes.
 
 
-use std::fmt::Arguments;
-
-use dioxus_core::{nodes::Attribute, DioxusElement, NodeFactory};
-
-macro_rules! no_namespace_trait_methods {
-    (
-        $(
-            $(#[$attr:meta])*
-            $name:ident;
-        )*
-    ) => {
-        $(
-            $(#[$attr])*
-            fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-                cx.attr(stringify!($name), val, None, false)
-            }
-        )*
-    };
-}
-macro_rules! style_trait_methods {
-    (
-        $(
-            $(#[$attr:meta])*
-            $name:ident: $lit:literal,
-        )*
-    ) => {
-        $(
-            #[inline]
-            $(#[$attr])*
-            fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-                cx.attr($lit, val, Some("style"), false)
-            }
-        )*
-    };
-}
-macro_rules! aria_trait_methods {
-    (
-        $(
-            $(#[$attr:meta])*
-            $name:ident: $lit:literal,
-        )*
-    ) => {
-        $(
-            $(#[$attr])*
-            fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-                cx.attr($lit, val, None, false)
-            }
-        )*
-    };
-}
-
-pub trait GlobalAttributes {
-    no_namespace_trait_methods! {
-        accesskey;
-
-        /// The HTML class attribute is used to specify a class for an HTML element.
-        ///
-        /// ## Details
-        /// Multiple HTML elements can share the same class.
-        ///
-        /// The class global attribute is a space-separated list of the case-sensitive classes of the element.
-        /// Classes allow CSS and Javascript to select and access specific elements via the class selectors or
-        /// functions like the DOM method document.getElementsByClassName.
-        ///
-        /// ## Example
-        ///
-        /// ### HTML:
-        /// ```html
-        /// <p class="note editorial">Above point sounds a bit obvious. Remove/rewrite?</p>
-        /// ```
-        ///
-        /// ### CSS:
-        /// ```css
-        /// .note {
-        ///     font-style: italic;
-        ///     font-weight: bold;
-        /// }
-        ///
-        /// .editorial {
-        ///     background: rgb(255, 0, 0, .25);
-        ///     padding: 10px;
-        /// }
-        /// ```
-        class;
-        contenteditable;
-        data;
-        dir;
-        draggable;
-        hidden;
-        id;
-        lang;
-        spellcheck;
-        style;
-        tabindex;
-        title;
-        translate;
-
-        /// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting
-        /// HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS)
-        /// attack. So, you can set HTML directly from Dioxus, but you have to type out dangerous_inner_html to remind
-        /// yourself that it’s dangerous
-        dangerous_inner_html;
-    }
-
-    // This macro creates an explicit method call for each of the style attributes.
-    //
-    // The left token specifies the name of the attribute in the rsx! macro, and the right string literal specifies the
-    // actual name of the attribute generated.
-    //
-    // This roughly follows the html spec
-    style_trait_methods! {
-        /// Specifies the alignment of flexible container's items within the flex container.
-        align_content: "align-content",
-
-        /// Specifies the default alignment for items within the flex container.
-        align_items: "align-items",
-
-        /// Specifies the alignment for selected items within the flex container.
-        align_self: "align-self",
-
-        /// Specifies the keyframe_based animations.
-        animation: "animation",
-
-        /// Specifies when the animation will start.
-        animation_delay: "animation-delay",
-
-        /// Specifies whether the animation should play in reverse on alternate cycles or not.
-        animation_direction: "animation-direction",
-
-        /// Specifies the number of seconds or milliseconds an animation should take to complete one cycle
-        animation_duration: "animation-duration",
-
-        /// Specifies how a CSS animation should apply styles to its target before and after it is executing
-        animation_fill_mode: "animation-fill-mode",
-
-        /// Specifies the number of times an animation cycle should be played before stopping.
-        animation_iteration_count: "animation-iteration-count",
-
-        /// Specifies the name of @keyframes defined animations that should be applied to the selected element
-        animation_name: "animation-name",
-
-        /// Specifies whether the animation is running or paused.
-        animation_play_state: "animation-play-state",
-
-        /// Specifies how a CSS animation should progress over the duration of each cycle.
-        animation_timing_function: "animation-timing-function",
-
-        /// Specifies whether or not the "back" side of a transformed element is visible when facing the user.
-        backface_visibility: "backface-visibility",
-
-        /// Defines a variety of background properties within one declaration.
-        background: "background",
-
-        /// Specify whether the background image is fixed in the viewport or scrolls.
-        background_attachment: "background-attachment",
-
-        /// Specifies the painting area of the background.
-        background_clip: "background-clip",
-
-        /// Defines an element's background color.
-        background_color: "background-color",
-
-        /// Defines an element's background image.
-        background_image: "background-image",
-
-        /// Specifies the positioning area of the background images.
-        background_origin: "background-origin",
-
-        /// Defines the origin of a background image.
-        background_position: "background-position",
-
-        /// Specify whether/how the background image is tiled.
-        background_repeat: "background-repeat",
-
-        /// Specifies the size of the background images.
-        background_size: "background-size",
-
-        /// Sets the width, style, and color for all four sides of an element's border.
-        border: "border",
-
-        /// Sets the width, style, and color of the bottom border of an element.
-        border_bottom: "border-bottom",
-
-        /// Sets the color of the bottom border of an element.
-        border_bottom_color: "border-bottom-color",
-
-        /// Defines the shape of the bottom_left border corner of an element.
-        border_bottom_left_radius: "border-bottom-left-radius",
-
-        /// Defines the shape of the bottom_right border corner of an element.
-        border_bottom_right_radius: "border-bottom-right-radius",
-
-        /// Sets the style of the bottom border of an element.
-        border_bottom_style: "border-bottom-style",
-
-        /// Sets the width of the bottom border of an element.
-        border_bottom_width: "border-bottom-width",
-
-        /// Specifies whether table cell borders are connected or separated.
-        border_collapse: "border-collapse",
-
-        /// Sets the color of the border on all the four sides of an element.
-        border_color: "border-color",
-
-        /// Specifies how an image is to be used in place of the border styles.
-        border_image: "border-image",
-
-        /// Specifies the amount by which the border image area extends beyond the border box.
-        border_image_outset: "border-image-outset",
-
-        /// Specifies whether the image_border should be repeated, rounded or stretched.
-        border_image_repeat: "border-image-repeat",
-
-        /// Specifies the inward offsets of the image_border.
-        border_image_slice: "border-image-slice",
-
-        /// Specifies the location of the image to be used as a border.
-        border_image_source: "border-image-source",
-
-        /// Specifies the width of the image_border.
-        border_image_width: "border-image-width",
-
-        /// Sets the width, style, and color of the left border of an element.
-        border_left: "border-left",
-
-        /// Sets the color of the left border of an element.
-        border_left_color: "border-left-color",
-
-        /// Sets the style of the left border of an element.
-        border_left_style: "border-left-style",
-
-        /// Sets the width of the left border of an element.
-        border_left_width: "border-left-width",
-
-        /// Defines the shape of the border corners of an element.
-        border_radius: "border-radius",
-
-        /// Sets the width, style, and color of the right border of an element.
-        border_right: "border-right",
-
-        /// Sets the color of the right border of an element.
-        border_right_color: "border-right-color",
-
-        /// Sets the style of the right border of an element.
-        border_right_style: "border-right-style",
-
-        /// Sets the width of the right border of an element.
-        border_right_width: "border-right-width",
-
-        /// Sets the spacing between the borders of adjacent table cells.
-        border_spacing: "border-spacing",
-
-        /// Sets the style of the border on all the four sides of an element.
-        border_style: "border-style",
-
-        /// Sets the width, style, and color of the top border of an element.
-        border_top: "border-top",
-
-        /// Sets the color of the top border of an element.
-        border_top_color: "border-top-color",
-
-        /// Defines the shape of the top_left border corner of an element.
-        border_top_left_radius: "border-top-left-radius",
-
-        /// Defines the shape of the top_right border corner of an element.
-        border_top_right_radius: "border-top-right-radius",
-
-        /// Sets the style of the top border of an element.
-        border_top_style: "border-top-style",
-
-        /// Sets the width of the top border of an element.
-        border_top_width: "border-top-width",
-
-        /// Sets the width of the border on all the four sides of an element.
-        border_width: "border-width",
-
-        /// Specify the location of the bottom edge of the positioned element.
-        bottom: "bottom",
-
-        /// Applies one or more drop_shadows to the element's box.
-        box_shadow: "box-shadow",
-
-        /// Alter the default CSS box model.
-        box_sizing: "box-sizing",
-
-        /// Specify the position of table's caption.
-        caption_side: "caption-side",
-
-        /// Specifies the placement of an element in relation to floating elements.
-        clear: "clear",
-
-        /// Defines the clipping region.
-        clip: "clip",
-
-        /// Specify the color of the text of an element.
-        color: "color",
-
-        /// Specifies the number of columns in a multi_column element.
-        column_count: "column-count",
-
-        /// Specifies how columns will be filled.
-        column_fill: "column-fill",
-
-        /// Specifies the gap between the columns in a multi_column element.
-        column_gap: "column-gap",
-
-        /// Specifies a straight line, or "rule", to be drawn between each column in a multi_column element.
-        column_rule: "column-rule",
-
-        /// Specifies the color of the rules drawn between columns in a multi_column layout.
-        column_rule_color: "column-rule-color",
-
-        /// Specifies the style of the rule drawn between the columns in a multi_column layout.
-        column_rule_style: "column-rule-style",
-
-        /// Specifies the width of the rule drawn between the columns in a multi_column layout.
-        column_rule_width: "column-rule-width",
-
-        /// Specifies how many columns an element spans across in a multi_column layout.
-        column_span: "column-span",
-
-        /// Specifies the optimal width of the columns in a multi_column element.
-        column_width: "column-width",
-
-        /// A shorthand property for setting column_width and column_count properties.
-        columns: "columns",
-
-        /// Inserts generated content.
-        content: "content",
-
-        /// Increments one or more counter values.
-        counter_increment: "counter-increment",
-
-        /// Creates or resets one or more counters.
-        counter_reset: "counter-reset",
-
-        /// Specify the type of cursor.
-        cursor: "cursor",
-
-        /// Define the text direction/writing direction.
-        direction: "direction",
-
-        /// Specifies how an element is displayed onscreen.
-        display: "display",
-
-        /// Show or hide borders and backgrounds of empty table cells.
-        empty_cells: "empty-cells",
-
-        /// Specifies the components of a flexible length.
-        flex: "flex",
-
-        /// Specifies the initial main size of the flex item.
-        flex_basis: "flex-basis",
-
-        /// Specifies the direction of the flexible items.
-        flex_direction: "flex-direction",
-
-        /// A shorthand property for the flex_direction and the flex_wrap properties.
-        flex_flow: "flex-flow",
-
-        /// Specifies how the flex item will grow relative to the other items inside the flex container.
-        flex_grow: "flex-grow",
-
-        /// Specifies how the flex item will shrink relative to the other items inside the flex container
-        flex_shrink: "flex-shrink",
-
-        /// Specifies whether the flexible items should wrap or not.
-        flex_wrap: "flex-wrap",
-
-        /// Specifies whether or not a box should float.
-        float: "float",
-
-        /// Defines a variety of font properties within one declaration.
-        font: "font",
-
-        /// Defines a list of fonts for element.
-        font_family: "font-family",
-
-        /// Defines the font size for the text.
-        font_size: "font-size",
-
-        /// Preserves the readability of text when font fallback occurs.
-        font_size_adjust: "font-size-adjust",
-
-        /// Selects a normal, condensed, or expanded face from a font.
-        font_stretch: "font-stretch",
-
-        /// Defines the font style for the text.
-        font_style: "font-style",
-
-        /// Specify the font variant.
-        font_variant: "font-variant",
-
-        /// Specify the font weight of the text.
-        font_weight: "font-weight",
-
-        /// Specify the height of an element.
-        height: "height",
-
-        /// Specifies how flex items are aligned along the main axis of the flex container after any flexible lengths and auto margins have been resolved.
-        justify_content: "auto margins have been resolved.",
-
-        /// Specify the location of the left edge of the positioned element.
-        left: "left",
-
-        /// Sets the extra spacing between letters.
-        letter_spacing: "letter-spacing",
-
-        /// Sets the height between lines of text.
-        line_height: "line-height",
-
-        /// Defines the display style for a list and list elements.
-        list_style: "list-style",
-
-        /// Specifies the image to be used as a list_item marker.
-        list_style_image: "list-style-image",
-
-        /// Specifies the position of the list_item marker.
-        list_style_position: "list-style-position",
-
-        /// Specifies the marker style for a list_item.
-        list_styler_type: "list-style-type",
-
-        /// Sets the margin on all four sides of the element.
-        margin: "margin",
-
-        /// Sets the bottom margin of the element.
-        margin_bottom: "margin-bottom",
-
-        /// Sets the left margin of the element.
-        margin_left: "margin-left",
-
-        /// Sets the right margin of the element.
-        margin_right: "margin-right",
-
-        /// Sets the top margin of the element.
-        margin_top: "margin-top",
-
-        /// Specify the maximum height of an element.
-        max_height: "max-height",
-
-        /// Specify the maximum width of an element.
-        max_width: "max-width",
-
-        /// Specify the minimum height of an element.
-        min_height: "min-height",
-
-        /// Specify the minimum width of an element.
-        min_width: "min-width",
-
-        /// Specifies the transparency of an element.
-        opacity: "opacity",
-
-        /// Specifies the order in which a flex items are displayed and laid out within a flex container.
-        order: "order",
-
-        /// Sets the width, style, and color for all four sides of an element's outline.
-        outline: "outline",
-
-        /// Sets the color of the outline.
-        outline_color: "outline-color",
-
-        /// Set the space between an outline and the border edge of an element.
-        outline_offset: "outline-offset",
-
-        /// Sets a style for an outline.
-        outline_style: "outline-style",
-
-        /// Sets the width of the outline.
-        outline_width: "outline-width",
-
-        /// Specifies the treatment of content that overflows the element's box.
-        overflow: "overflow",
-
-        /// Specifies the treatment of content that overflows the element's box horizontally.
-        overflow_x: "overflow-x",
-
-        /// Specifies the treatment of content that overflows the element's box vertically.
-        overflow_y: "overflow-y",
-
-        /// Sets the padding on all four sides of the element.
-        padding: "padding",
-
-        /// Sets the padding to the bottom side of an element.
-        padding_bottom: "padding-bottom",
-
-        /// Sets the padding to the left side of an element.
-        padding_left: "padding-left",
-
-        /// Sets the padding to the right side of an element.
-        padding_right: "padding-right",
-
-        /// Sets the padding to the top side of an element.
-        padding_top: "padding-top",
-
-        /// Insert a page breaks after an element.
-        page_break_after: "page-break-after",
-
-        /// Insert a page breaks before an element.
-        page_break_before: "page-break-before",
-
-        /// Insert a page breaks inside an element.
-        page_break_inside: "page-break-inside",
-
-        /// Defines the perspective from which all child elements of the object are viewed.
-        perspective: "perspective",
-
-        /// Defines the origin (the vanishing point for the 3D space) for the perspective property.
-        perspective_origin: "perspective-origin",
-
-        /// Specifies how an element is positioned.
-        position: "position",
-
-        /// The pointer-events CSS property sets under what circumstances (if any) a particular graphic element can
-        /// become the target of pointer events.
-        ///
-        /// MDN: [`pointer_events`](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events)
-        pointer_events: "pointer-events",
-
-        /// Specifies quotation marks for embedded quotations.
-        quotes: "quotes",
-
-        /// Specifies whether or not an element is resizable by the user.
-        resize: "resize",
-
-        /// Specify the location of the right edge of the positioned element.
-        right: "right",
-
-        /// Specifies the length of the tab character.
-        tab_size: "tab-size",
-
-        /// Specifies a table layout algorithm.
-        table_layout: "table-layout",
-
-        /// Sets the horizontal alignment of inline content.
-        text_align: "text-align",
-        /// Specifies how the last line of a block or a line right before a forced line break is aligned when  is justify.",
-        text_align_last: "text-align-last",
-
-        /// Specifies the decoration added to text.
-        text_decoration: "text-decoration",
-
-        /// Specifies the color of the text_decoration_line.
-        text_decoration_color: "text-decoration-color",
-
-        /// Specifies what kind of line decorations are added to the element.
-        text_decoration_line: "text-decoration-line",
-
-        /// Specifies the style of the lines specified by the text_decoration_line property
-        text_decoration_style: "text-decoration-style",
-
-        /// Indent the first line of text.
-        text_indent: "text-indent",
-
-        /// Specifies the justification method to use when the text_align property is set to justify.
-        text_justify: "text-justify",
-
-        /// Specifies how the text content will be displayed, when it overflows the block containers.
-        text_overflow: "text-overflow",
-
-        /// Applies one or more shadows to the text content of an element.
-        text_shadow: "text-shadow",
-
-        /// Transforms the case of the text.
-        text_transform: "text-transform",
-
-        /// Specify the location of the top edge of the positioned element.
-        top: "top",
-
-        /// Applies a 2D or 3D transformation to an element.
-        transform: "transform",
-
-        /// Defines the origin of transformation for an element.
-        transform_origin: "transform-origin",
-
-        /// Specifies how nested elements are rendered in 3D space.
-        transform_style: "transform-style",
-
-        /// Defines the transition between two states of an element.
-        transition: "transition",
-
-        /// Specifies when the transition effect will start.
-        transition_delay: "transition-delay",
-
-        /// Specifies the number of seconds or milliseconds a transition effect should take to complete.
-        transition_duration: "transition-duration",
-
-        /// Specifies the names of the CSS properties to which a transition effect should be applied.
-        transition_property: "transition-property",
-
-        /// Specifies the speed curve of the transition effect.
-        transition_timing_function: "transition-timing-function",
-
-        /// Sets the vertical positioning of an element relative to the current text baseline.
-        vertical_align: "vertical-align",
-
-        /// Specifies whether or not an element is visible.
-        visibility: "visibility",
-
-        /// Specifies how white space inside the element is handled.
-        white_space: "white-space",
-
-        /// Specify the width of an element.
-        width: "width",
-
-        /// Specifies how to break lines within words.
-        word_break: "word-break",
-
-        /// Sets the spacing between words.
-        word_spacing: "word-spacing",
-
-        /// Specifies whether to break words when the content overflows the boundaries of its container.
-        word_wrap: "word-wrap",
-
-        /// Specifies a layering or stacking order for positioned elements.
-        z_index	: "z-index	",
-
-    }
-    aria_trait_methods! {
-        aria_current: "aria-current",
-        aria_details: "aria-details",
-        aria_disabled: "aria-disabled",
-        aria_hidden: "aria-hidden",
-        aria_invalid: "aria-invalid",
-        aria_keyshortcuts: "aria-keyshortcuts",
-        aria_label: "aria-label",
-        aria_roledescription: "aria-roledescription",
-        // Widget Attributes
-        aria_autocomplete: "aria-autocomplete",
-        aria_checked: "aria-checked",
-        aria_expanded: "aria-expanded",
-        aria_haspopup: "aria-haspopup",
-        aria_level: "aria-level",
-        aria_modal: "aria-modal",
-        aria_multiline: "aria-multiline",
-        aria_multiselectable: "aria-multiselectable",
-        aria_orientation: "aria-orientation",
-        aria_placeholder: "aria-placeholder",
-        aria_pressed: "aria-pressed",
-        aria_readonly: "aria-readonly",
-        aria_required: "aria-required",
-        aria_selected: "aria-selected",
-        aria_sort: "aria-sort",
-        aria_valuemax: "aria-valuemax",
-        aria_valuemin: "aria-valuemin",
-        aria_valuenow: "aria-valuenow",
-        aria_valuetext: "aria-valuetext",
-        // Live Region Attributes
-        aria_atomic: "aria-atomic",
-        aria_busy: "aria-busy",
-        aria_live: "aria-live",
-        aria_relevant: "aria-relevant",
-
-        aria_dropeffect: "aria-dropeffect",
-        aria_grabbed: "aria-grabbed",
-        // Relationship Attributes
-        aria_activedescendant: "aria-activedescendant",
-        aria_colcount: "aria-colcount",
-        aria_colindex: "aria-colindex",
-        aria_colspan: "aria-colspan",
-        aria_controls: "aria-controls",
-        aria_describedby: "aria-describedby",
-        aria_errormessage: "aria-errormessage",
-        aria_flowto: "aria-flowto",
-        aria_labelledby: "aria-labelledby",
-        aria_owns: "aria-owns",
-        aria_posinset: "aria-posinset",
-        aria_rowcount: "aria-rowcount",
-        aria_rowindex: "aria-rowindex",
-        aria_rowspan: "aria-rowspan",
-        aria_setsize: "aria-setsize",
-    }
-}
-
-macro_rules! builder_constructors {
-    (
-        $(
-            $(#[$attr:meta])*
-            $name:ident {
-                $(
-                    $(#[$attr_method:meta])*
-                    $fil:ident: $vil:ident,
-                )*
-            };
-         )*
-    ) => {
-        $(
-            #[allow(non_camel_case_types)]
-            $(#[$attr])*
-            pub struct $name;
-
-            impl DioxusElement for $name {
-                const TAG_NAME: &'static str = stringify!($name);
-                const NAME_SPACE: Option<&'static str> = None;
-            }
-
-            impl GlobalAttributes for $name {}
-
-            impl $name {
-                $(
-                    $(#[$attr_method])*
-                    pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-                        cx.attr(stringify!($fil), val, None, false)
-                    }
-                )*
-            }
-        )*
-    };
-
-    ( $(
-        $(#[$attr:meta])*
-        $name:ident <> $namespace:tt {
-            $($fil:ident: $vil:ident,)*
-        };
-    )* ) => {
-        $(
-            #[allow(non_camel_case_types)]
-            $(#[$attr])*
-            pub struct $name;
-
-            impl DioxusElement for $name {
-                const TAG_NAME: &'static str = stringify!($name);
-                const NAME_SPACE: Option<&'static str> = Some($namespace);
-            }
-
-            impl SvgAttributes for $name {}
-
-            impl $name {
-                $(
-                    pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-                        cx.attr(stringify!($fil), val, Some(stringify!($namespace)), false)
-                    }
-                )*
-            }
-        )*
-    };
-}
-
-// Organized in the same order as
-// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
-//
-// Does not include obsolete elements.
-//
-// This namespace represents a collection of modern HTML-5 compatiable elements.
-//
-// This list does not include obsolete, deprecated, experimental, or poorly supported elements.
-builder_constructors! {
-    // Document metadata
-
-    /// Build a
-    /// [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
-    /// element.
-    ///
-    base {
-        href: Uri,
-        target: Target,
-    };
-
-    /// Build a
-    /// [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
-    /// element.
-    head {};
-
-    /// Build a
-    /// [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
-    /// element.
-    link {
-        // as: Mime,
-        crossorigin: CrossOrigin,
-        href: Uri,
-        hreflang: LanguageTag,
-        media: String, // FIXME media query
-        rel: LinkType,
-        sizes: String, // FIXME
-        title: String, // FIXME
-        r#type: Mime,
-        integrity: String,
-    };
-
-    /// Build a
-    /// [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
-    /// element.
-    meta {
-        charset: String, // FIXME IANA standard names
-        content: String,
-        http_equiv: HTTPEquiv,
-        name: Metadata,
-    };
-
-    /// Build a
-    /// [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
-    /// element.
-    style {
-        r#type: Mime,
-        media: String, // FIXME media query
-        nonce: Nonce,
-        title: String, // FIXME
-    };
-
-    /// Build a
-    /// [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
-    /// element.
-    title { };
-
-    // Sectioning root
-
-    /// Build a
-    /// [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
-    /// element.
-    body {};
-
-    // ------------------
-    // Content sectioning
-    // ------------------
-
-    /// Build a
-    /// [`<address>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address)
-    /// element.
-    address {};
-
-    /// Build a
-    /// [`<article>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article)
-    /// element.
-    article {};
-
-    /// Build a
-    /// [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
-    /// element.
-    aside {};
-
-    /// Build a
-    /// [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)
-    /// element.
-    footer {};
-
-    /// Build a
-    /// [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)
-    /// element.
-    header {};
-
-    /// Build a
-    /// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
-    /// element.
-    ///
-    /// # About
-    /// - The HTML `<h1>` element is found within the `<body>` tag.
-    /// - Headings can range from `<h1>` to `<h6>`.
-    /// - The most important heading is `<h1>` and the least important heading is `<h6>`.
-    /// - The `<h1>` heading is the first heading in the document.
-    /// - The `<h1>` heading is usually a large bolded font.
-    ///
-    /// # Usage
-    ///
-    /// ```
-    /// html!(<h1> A header element </h1>)
-    /// rsx!(h1 { "A header element" })
-    /// LazyNodes::new(|f| f.el(h1).children([f.text("A header element")]).finish())
-    /// ```
-    h1 {};
-
-
-    /// Build a
-    /// [`<h2>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2)
-    /// element.
-    ///
-    /// # About
-    /// - The HTML `<h2>` element is found within the `<body>` tag.
-    /// - Headings can range from `<h1>` to `<h6>`.
-    /// - The most important heading is `<h1>` and the least important heading is `<h6>`.
-    /// - The `<h2>` heading is the second heading in the document.
-    /// - The `<h2>` heading is usually a large bolded font.
-    ///
-    /// # Usage
-    /// ```
-    /// html!(<h2> A header element </h2>)
-    /// rsx!(h2 { "A header element" })
-    /// LazyNodes::new(|f| f.el(h2).children([f.text("A header element")]).finish())
-    /// ```
-    h2 {};
-
-
-    /// Build a
-    /// [`<h3>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3)
-    /// element.
-    ///
-    /// # About
-    /// - The HTML <h1> element is found within the <body> tag.
-    /// - Headings can range from <h1> to <h6>.
-    /// - The most important heading is <h1> and the least important heading is <h6>.
-    /// - The <h1> heading is the first heading in the document.
-    /// - The <h1> heading is usually a large bolded font.
-    h3 {};
-    /// Build a
-    /// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
-    /// element.
-    h4 {};
-    /// Build a
-    /// [`<h5>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5)
-    /// element.
-    h5 {};
-    /// Build a
-    /// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
-    /// element.
-    h6 {};
-
-    /// Build a
-    /// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
-    /// element.
-    main {};
-    /// Build a
-    /// [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)
-    /// element.
-    nav {};
-    /// Build a
-    /// [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)
-    /// element.
-    section {};
-
-    // Text content
-
-    /// Build a
-    /// [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)
-    /// element.
-    blockquote {
-        cite: Uri,
-    };
-    /// Build a
-    /// [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)
-    /// element.
-    dd {};
-
-    /// Build a
-    /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
-    /// element.
-    ///
-    /// Part of the HTML namespace. Only works in HTML-compatible renderers
-    ///
-    /// ## Definition and Usage
-    /// - The <div> tag defines a division or a section in an HTML document.
-    /// - The <div> tag is used as a container for HTML elements - which is then styled with CSS or manipulated with  JavaScript.
-    /// - The <div> tag is easily styled by using the class or id attribute.
-    /// - Any sort of content can be put inside the <div> tag!
-    ///
-    /// Note: By default, browsers always place a line break before and after the <div> element.
-    ///
-    /// ## Usage
-    /// ```
-    /// html!(<div> A header element </div>)
-    /// rsx!(div { "A header element" })
-    /// LazyNodes::new(|f| f.element(div, &[], &[], &[], None))
-    /// ```
-    ///
-    /// ## References:
-    /// - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div
-    /// - https://www.w3schools.com/tags/tag_div.asp
-    div {};
-
-    /// Build a
-    /// [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)
-    /// element.
-    dl {};
-
-    /// Build a
-    /// [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)
-    /// element.
-    dt {};
-
-    /// Build a
-    /// [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)
-    /// element.
-    figcaption {};
-
-    /// Build a
-    /// [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)
-    /// element.
-    figure {};
-
-    /// Build a
-    /// [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)
-    /// element.
-    hr {};
-
-    /// Build a
-    /// [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
-    /// element.
-    li {
-        value: isize,
-    };
-
-    /// Build a
-    /// [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)
-    /// element.
-    ol {
-        reversed: Bool,
-        start: isize,
-        r#type: OrderedListType,
-    };
-
-    /// Build a
-    /// [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)
-    /// element.
-    p {};
-
-    /// Build a
-    /// [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)
-    /// element.
-    pre {};
-
-    /// Build a
-    /// [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
-    /// element.
-    ul {};
-
-
-    // Inline text semantics
-
-    /// Build a
-    /// [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
-    /// element.
-    a {
-        download: String,
-        href: Uri,
-        hreflang: LanguageTag,
-        target: Target,
-        r#type: Mime,
-        // ping: SpacedList<Uri>,
-        // rel: SpacedList<LinkType>,
-        ping: SpacedList,
-        rel: SpacedList,
-    };
-
-    /// Build a
-    /// [`<abbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr)
-    /// element.
-    abbr {};
-
-    /// Build a
-    /// [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)
-    /// element.
-    b {};
-
-    /// Build a
-    /// [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)
-    /// element.
-    bdi {};
-
-    /// Build a
-    /// [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)
-    /// element.
-    bdo {};
-
-    /// Build a
-    /// [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)
-    /// element.
-    br {};
-
-    /// Build a
-    /// [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)
-    /// element.
-    cite {};
-
-    /// Build a
-    /// [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)
-    /// element.
-    code {
-        language: String,
-    };
-
-    /// Build a
-    /// [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)
-    /// element.
-    data {
-        value: String,
-    };
-
-    /// Build a
-    /// [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)
-    /// element.
-    dfn {};
-
-    /// Build a
-    /// [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)
-    /// element.
-    em {};
-
-    /// Build a
-    /// [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)
-    /// element.
-    i {};
-
-    /// Build a
-    /// [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)
-    /// element.
-    kbd {};
-
-    /// Build a
-    /// [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)
-    /// element.
-    mark {};
-
-    /// Build a
-    /// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
-    /// element.
-    q {
-        cite: Uri,
-    };
-
-
-    /// Build a
-    /// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
-    /// element.
-    rp {};
-
-
-    /// Build a
-    /// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
-    /// element.
-    rt {};
-
-
-    /// Build a
-    /// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
-    /// element.
-    ruby {};
-
-    /// Build a
-    /// [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)
-    /// element.
-    s {};
-
-    /// Build a
-    /// [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)
-    /// element.
-    samp {};
-
-    /// Build a
-    /// [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)
-    /// element.
-    small {};
-
-    /// Build a
-    /// [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)
-    /// element.
-    span {};
-
-    /// Build a
-    /// [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)
-    /// element.
-    strong {};
-
-    /// Build a
-    /// [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)
-    /// element.
-    sub {};
-
-    /// Build a
-    /// [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)
-    /// element.
-    sup {};
-
-    /// Build a
-    /// [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)
-    /// element.
-    time {};
-
-    /// Build a
-    /// [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)
-    /// element.
-    u {};
-
-    /// Build a
-    /// [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)
-    /// element.
-    var {};
-
-    /// Build a
-    /// [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)
-    /// element.
-    wbr {};
-
-
-    // Image and multimedia
-
-    /// Build a
-    /// [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
-    /// element.
-    area {
-        alt: String,
-        coords: String, // TODO could perhaps be validated
-        download: Bool,
-        href: Uri,
-        hreflang: LanguageTag,
-        shape: AreaShape,
-        target: Target,
-        // ping: SpacedList<Uri>,
-        // rel: SpacedSet<LinkType>,
-    };
-
-    /// Build a
-    /// [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
-    /// element.
-    audio {
-        autoplay: Bool,
-        controls: Bool,
-        crossorigin: CrossOrigin,
-        muted: Bool,
-        preload: Preload,
-        src: Uri,
-        r#loop: Bool,
-    };
-
-    /// Build a
-    /// [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
-    /// element.
-    img {
-        alt: String,
-        crossorigin: CrossOrigin,
-        decoding: ImageDecoding,
-        height: usize,
-        ismap: Bool,
-        src: Uri,
-        srcset: String, // FIXME this is much more complicated
-        usemap: String, // FIXME should be a fragment starting with '#'
-        width: usize,
-        referrerpolicy: String,
-        // sizes: SpacedList<String>, // FIXME it's not really just a string
-    };
-
-    /// Build a
-    /// [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)
-    /// element.
-    map {
-        name: Id,
-    };
-
-    /// Build a
-    /// [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)
-    /// element.
-    track {
-        default: Bool,
-        kind: VideoKind,
-        label: String,
-        src: Uri,
-        srclang: LanguageTag,
-    };
-
-    /// Build a
-    /// [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
-    /// element.
-    video {
-        autoplay: Bool,
-        controls: Bool,
-        crossorigin: CrossOrigin,
-        height: usize,
-        r#loop: Bool,
-        muted: Bool,
-        preload: Preload,
-        playsinline: Bool,
-        poster: Uri,
-        src: Uri,
-        width: usize,
-    };
-
-
-    // Embedded content
-
-    /// Build a
-    /// [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)
-    /// element.
-    embed {
-        height: usize,
-        src: Uri,
-        r#type: Mime,
-        width: usize,
-    };
-
-    /// Build a
-    /// [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
-    /// element.
-    iframe {
-        allow: FeaturePolicy,
-        allowfullscreen: Bool,
-        allowpaymentrequest: Bool,
-        height: usize,
-        name: Id,
-        referrerpolicy: ReferrerPolicy,
-        src: Uri,
-        srcdoc: Uri,
-        width: usize,
-
-        marginWidth: String,
-        align: String,
-        longdesc: String,
-
-        scrolling: String,
-        marginHeight: String,
-        frameBorder: String,
-        // sandbox: SpacedSet<Sandbox>,
-    };
-
-    /// Build a
-    /// [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)
-    /// element.
-    object {
-        data: Uri,
-        form: Id,
-        height: usize,
-        name: Id,
-        r#type: Mime,
-        typemustmatch: Bool,
-        usemap: String, // TODO should be a fragment starting with '#'
-        width: usize,
-    };
-
-    /// Build a
-    /// [`<param>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param)
-    /// element.
-    param {
-        name: String,
-        value: String,
-    };
-
-    /// Build a
-    /// [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
-    /// element.
-    picture {};
-
-    /// Build a
-    /// [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)
-    /// element.
-    source {
-        src: Uri,
-        r#type: Mime,
-    };
-
-
-    // Scripting
-
-    /// Build a
-    /// [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
-    /// element.
-    canvas {
-        height: usize,
-        width: usize,
-    };
-
-    /// Build a
-    /// [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)
-    /// element.
-    noscript {};
-
-    /// Build a
-    /// [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
-    /// element.
-    script {
-        crossorigin: CrossOrigin,
-        defer: Bool,
-        integrity: Integrity,
-        nomodule: Bool,
-        nonce: Nonce,
-        src: Uri,
-        text: String,
-        r#async: Bool,
-        r#type: String, // TODO could be an enum
-    };
-
-
-    // Demarcating edits
-
-    /// Build a
-    /// [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)
-    /// element.
-    del {
-        cite: Uri,
-        datetime: Datetime,
-    };
-
-    /// Build a
-    /// [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)
-    /// element.
-    ins {
-        cite: Uri,
-        datetime: Datetime,
-    };
-
-
-    // Table content
-
-    /// Build a
-    /// [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)
-    /// element.
-    caption {};
-
-    /// Build a
-    /// [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)
-    /// element.
-    col {
-        span: usize,
-    };
-
-    /// Build a
-    /// [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
-    /// element.
-    colgroup {
-        span: usize,
-    };
-
-    /// Build a
-    /// [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)
-    /// element.
-    table {};
-
-    /// Build a
-    /// [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)
-    /// element.
-    tbody {};
-
-    /// Build a
-    /// [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
-    /// element.
-    td {
-        colspan: usize,
-        rowspan: usize,
-        // headers: SpacedSet<Id>,
-    };
-
-    /// Build a
-    /// [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)
-    /// element.
-    tfoot {};
-
-    /// Build a
-    /// [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)
-    /// element.
-    th {
-        abbr: String,
-        colspan: usize,
-        rowspan: usize,
-        scope: TableHeaderScope,
-        // headers: SpacedSet<Id>,
-    };
-
-    /// Build a
-    /// [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)
-    /// element.
-    thead {};
-
-    /// Build a
-    /// [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)
-    /// element.
-    tr {};
-
-
-    // Forms
-
-    /// Build a
-    /// [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
-    /// element.
-    button {
-        autofocus: Bool,
-        disabled: Bool,
-        form: Id,
-        formaction: Uri,
-        formenctype: FormEncodingType,
-        formmethod: FormMethod,
-        formnovalidate: Bool,
-        formtarget: Target,
-        name: Id,
-        r#type: ButtonType,
-        value: String,
-    };
-
-    /// Build a
-    /// [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)
-    /// element.
-    datalist {};
-
-    /// Build a
-    /// [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)
-    /// element.
-    fieldset {};
-
-    /// Build a
-    /// [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
-    /// element.
-    form {
-        // accept-charset: SpacedList<CharacterEncoding>,
-        action: Uri,
-        autocomplete: OnOff,
-        enctype: FormEncodingType,
-        method: FormMethod,
-        name: Id,
-        novalidate: Bool,
-        target: Target,
-    };
-
-    /// Build a
-    /// [`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
-    /// element.
-    input {
-        accept: String,
-        alt: String,
-        autocomplete: String,
-        autofocus: Bool,
-        capture: String,
-        checked: Bool,
-        disabled: Bool,
-        form: Id,
-        formaction: Uri,
-        formenctype: FormEncodingType,
-        formmethod: FormDialogMethod,
-        formnovalidate: Bool,
-        formtarget: Target,
-        height: isize,
-        list: Id,
-        max: String,
-        maxlength: usize,
-        min: String,
-        minlength: usize,
-        multiple: Bool,
-        name: Id,
-        pattern: String,
-        placeholder: String,
-        readonly: Bool,
-        required: Bool,
-        size: usize,
-        spellcheck: Bool,
-        src: Uri,
-        step: String,
-        tabindex: usize,
-
-        width: isize,
-
-        // Manual implementations below...
-        // r#type: InputType,
-        // value: String,
-    };
-
-    /// Build a
-    /// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
-    /// element.
-    label {
-        form: Id,
-        // r#for: Id,
-    };
-
-    /// Build a
-    /// [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)
-    /// element.
-    legend {};
-
-    /// Build a
-    /// [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)
-    /// element.
-    meter {
-        value: isize,
-        min: isize,
-        max: isize,
-        low: isize,
-        high: isize,
-        optimum: isize,
-        form: Id,
-    };
-
-    /// Build a
-    /// [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)
-    /// element.
-    optgroup {
-        disabled: Bool,
-        label: String,
-    };
-
-    /// Build a
-    /// [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
-    /// element.
-    option {
-        disabled: Bool,
-        label: String,
-
-
-        value: String,
-
-        // defined below
-        // selected: Bool,
-    };
-
-    /// Build a
-    /// [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)
-    /// element.
-    output {
-        form: Id,
-        name: Id,
-        // r#for: SpacedSet<Id>,
-    };
-
-    /// Build a
-    /// [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
-    /// element.
-    progress {
-        max: f64,
-        value: f64,
-    };
-
-    /// Build a
-    /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
-    /// element.
-    select {
-        // defined below
-        // value: String,
-        autocomplete: String,
-        autofocus: Bool,
-        disabled: Bool,
-        form: Id,
-        multiple: Bool,
-        name: Id,
-        required: Bool,
-        size: usize,
-    };
-
-    /// Build a
-    /// [`<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
-    /// element.
-    textarea {
-        autocomplete: OnOff,
-        autofocus: Bool,
-        cols: usize,
-        disabled: Bool,
-        form: Id,
-        maxlength: usize,
-        minlength: usize,
-        name: Id,
-        placeholder: String,
-        readonly: Bool,
-        required: Bool,
-        rows: usize,
-        spellcheck: BoolOrDefault,
-        wrap: Wrap,
-    };
-
-
-    // Interactive elements
-
-    /// Build a
-    /// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
-    /// element.
-    details {
-        open: Bool,
-    };
-
-
-
-    /// Build a
-    /// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
-    /// element.
-    summary {};
-
-    // Web components
-
-    /// Build a
-    /// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
-    /// element.
-    slot {};
-
-    /// Build a
-    /// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
-    /// element.
-    template {};
-}
-
-impl input {
-    /// The type of input
-    ///
-    /// Here are the different input types you can use in HTML:
-    ///
-    /// - `button`
-    /// - `checkbox`
-    /// - `color`
-    /// - `date`
-    /// - `datetime-local`
-    /// - `email`
-    /// - `file`
-    /// - `hidden`
-    /// - `image`
-    /// - `month`
-    /// - `number`
-    /// - `password`
-    /// - `radio`
-    /// - `range`
-    /// - `reset`
-    /// - `search`
-    /// - `submit`
-    /// - `tel`
-    /// - `text`
-    /// - `time`
-    /// - `url`
-    /// - `week`    
-    pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-        cx.attr("type", val, None, false)
-    }
-
-    pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-        cx.attr("value", val, None, true)
-    }
-}
-
-/*
-volatile attributes
-*/
-
-impl select {
-    pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-        cx.attr("value", val, None, true)
-    }
-}
-
-impl option {
-    pub fn selected<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-        cx.attr("selected", val, None, true)
-    }
-}
-
-impl textarea {
-    pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-        cx.attr("value", val, None, true)
-    }
-}
-impl label {
-    pub fn r#for<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-        cx.attr("for", val, None, false)
-    }
-}
-
-pub trait SvgAttributes {
-    aria_trait_methods! {
-        accent_height: "accent-height",
-        accumulate: "accumulate",
-        additive: "additive",
-        alignment_baseline: "alignment-baseline",
-        alphabetic: "alphabetic",
-        amplitude: "amplitude",
-        arabic_form: "arabic-form",
-        ascent: "ascent",
-        attributeName: "attributeName",
-        attributeType: "attributeType",
-        azimuth: "azimuth",
-        baseFrequency: "baseFrequency",
-        baseline_shift: "baseline-shift",
-        baseProfile: "baseProfile",
-        bbox: "bbox",
-        begin: "begin",
-        bias: "bias",
-        by: "by",
-        calcMode: "calcMode",
-        cap_height: "cap-height",
-        class: "class",
-        clip: "clip",
-        clipPathUnits: "clipPathUnits",
-        clip_path: "clip-path",
-        clip_rule: "clip-rule",
-        color: "color",
-        color_interpolation: "color-interpolation",
-        color_interpolation_filters: "color-interpolation-filters",
-        color_profile: "color-profile",
-        color_rendering: "color-rendering",
-        contentScriptType: "contentScriptType",
-        contentStyleType: "contentStyleType",
-        crossorigin: "crossorigin",
-        cursor: "cursor",
-        cx: "cx",
-        cy: "cy",
-        d: "d",
-        decelerate: "decelerate",
-        descent: "descent",
-        diffuseConstant: "diffuseConstant",
-        direction: "direction",
-        display: "display",
-        divisor: "divisor",
-        dominant_baseline: "dominant-baseline",
-        dur: "dur",
-        dx: "dx",
-        dy: "dy",
-        edgeMode: "edgeMode",
-        elevation: "elevation",
-        enable_background: "enable-background",
-        end: "end",
-        exponent: "exponent",
-        fill: "fill",
-        fill_opacity: "fill-opacity",
-        fill_rule: "fill-rule",
-        filter: "filter",
-        filterRes: "filterRes",
-        filterUnits: "filterUnits",
-        flood_color: "flood-color",
-        flood_opacity: "flood-opacity",
-        font_family: "font-family",
-        font_size: "font-size",
-        font_size_adjust: "font-size-adjust",
-        font_stretch: "font-stretch",
-        font_style: "font-style",
-        font_variant: "font-variant",
-        font_weight: "font-weight",
-        format: "format",
-        from: "from",
-        fr: "fr",
-        fx: "fx",
-        fy: "fy",
-        g1: "g1",
-        g2: "g2",
-        glyph_name: "glyph-name",
-        glyph_orientation_horizontal: "glyph-orientation-horizontal",
-        glyph_orientation_vertical: "glyph-orientation-vertical",
-        glyphRef: "glyphRef",
-        gradientTransform: "gradientTransform",
-        gradientUnits: "gradientUnits",
-        hanging: "hanging",
-        height: "height",
-        href: "href",
-        hreflang: "hreflang",
-        horiz_adv_x: "horiz-adv-x",
-        horiz_origin_x: "horiz-origin-x",
-        id: "id",
-        ideographic: "ideographic",
-        image_rendering: "image-rendering",
-        _in: "_in",
-        in2: "in2",
-        intercept: "intercept",
-        k: "k",
-        k1: "k1",
-        k2: "k2",
-        k3: "k3",
-        k4: "k4",
-        kernelMatrix: "kernelMatrix",
-        kernelUnitLength: "kernelUnitLength",
-        kerning: "kerning",
-        keyPoints: "keyPoints",
-        keySplines: "keySplines",
-        keyTimes: "keyTimes",
-        lang: "lang",
-        lengthAdjust: "lengthAdjust",
-        letter_spacing: "letter-spacing",
-        lighting_color: "lighting-color",
-        limitingConeAngle: "limitingConeAngle",
-        local: "local",
-        marker_end: "marker-end",
-        marker_mid: "marker-mid",
-        marker_start: "marker_start",
-        markerHeight: "markerHeight",
-        markerUnits: "markerUnits",
-        markerWidth: "markerWidth",
-        mask: "mask",
-        maskContentUnits: "maskContentUnits",
-        maskUnits: "maskUnits",
-        mathematical: "mathematical",
-        max: "max",
-        media: "media",
-        method: "method",
-        min: "min",
-        mode: "mode",
-        name: "name",
-        numOctaves: "numOctaves",
-        offset: "offset",
-        opacity: "opacity",
-        operator: "operator",
-        order: "order",
-        orient: "orient",
-        orientation: "orientation",
-        origin: "origin",
-        overflow: "overflow",
-        overline_position: "overline-position",
-        overline_thickness: "overline-thickness",
-        panose_1: "panose-1",
-        paint_order: "paint-order",
-        path: "path",
-        pathLength: "pathLength",
-        patternContentUnits: "patternContentUnits",
-        patternTransform: "patternTransform",
-        patternUnits: "patternUnits",
-        ping: "ping",
-        pointer_events: "pointer-events",
-        points: "points",
-        pointsAtX: "pointsAtX",
-        pointsAtY: "pointsAtY",
-        pointsAtZ: "pointsAtZ",
-        preserveAlpha: "preserveAlpha",
-        preserveAspectRatio: "preserveAspectRatio",
-        primitiveUnits: "primitiveUnits",
-        r: "r",
-        radius: "radius",
-        referrerPolicy: "referrerPolicy",
-        refX: "refX",
-        refY: "refY",
-        rel: "rel",
-        rendering_intent: "rendering-intent",
-        repeatCount: "repeatCount",
-        repeatDur: "repeatDur",
-        requiredExtensions: "requiredExtensions",
-        requiredFeatures: "requiredFeatures",
-        restart: "restart",
-        result: "result",
-        rotate: "rotate",
-        rx: "rx",
-        ry: "ry",
-        scale: "scale",
-        seed: "seed",
-        shape_rendering: "shape-rendering",
-        slope: "slope",
-        spacing: "spacing",
-        specularConstant: "specularConstant",
-        specularExponent: "specularExponent",
-        speed: "speed",
-        spreadMethod: "spreadMethod",
-        startOffset: "startOffset",
-        stdDeviation: "stdDeviation",
-        stemh: "stemh",
-        stemv: "stemv",
-        stitchTiles: "stitchTiles",
-        stop_color: "stop_color",
-        stop_opacity: "stop_opacity",
-        strikethrough_position: "strikethrough-position",
-        strikethrough_thickness: "strikethrough-thickness",
-        string: "string",
-        stroke: "stroke",
-        stroke_dasharray: "stroke-dasharray",
-        stroke_dashoffset: "stroke-dashoffset",
-        stroke_linecap: "stroke-linecap",
-        stroke_linejoin: "stroke-linejoin",
-        stroke_miterlimit: "stroke-miterlimit",
-        stroke_opacity: "stroke-opacity",
-        stroke_width: "stroke-width",
-        style: "style",
-        surfaceScale: "surfaceScale",
-        systemLanguage: "systemLanguage",
-        tabindex: "tabindex",
-        tableValues: "tableValues",
-        target: "target",
-        targetX: "targetX",
-        targetY: "targetY",
-        text_anchor: "text-anchor",
-        text_decoration: "text-decoration",
-        text_rendering: "text-rendering",
-        textLength: "textLength",
-        to: "to",
-        transform: "transform",
-        transform_origin: "transform-origin",
-        r#type: "_type",
-        u1: "u1",
-        u2: "u2",
-        underline_position: "underline-position",
-        underline_thickness: "underline-thickness",
-        unicode: "unicode",
-        unicode_bidi: "unicode-bidi",
-        unicode_range: "unicode-range",
-        units_per_em: "units-per-em",
-        v_alphabetic: "v-alphabetic",
-        v_hanging: "v-hanging",
-        v_ideographic: "v-ideographic",
-        v_mathematical: "v-mathematical",
-        values: "values",
-        vector_effect: "vector-effect",
-        version: "version",
-        vert_adv_y: "vert-adv-y",
-        vert_origin_x: "vert-origin-x",
-        vert_origin_y: "vert-origin-y",
-        view_box: "viewBox",
-        view_target: "viewTarget",
-        visibility: "visibility",
-        width: "width",
-        widths: "widths",
-        word_spacing: "word-spacing",
-        writing_mode: "writing-mode",
-        x: "x",
-        x_height: "x-height",
-        x1: "x1",
-        x2: "x2",
-        xmlns: "xmlns",
-        x_channel_selector: "xChannelSelector",
-        y: "y",
-        y1: "y1",
-        y2: "y2",
-        y_channel_selector: "yChannelSelector",
-        z: "z",
-        zoomAndPan: "zoomAndPan",
-    }
-}
-
-builder_constructors! {
-    // SVG components
-    /// Build a
-    /// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
-    /// element.
-    svg <> "http://www.w3.org/2000/svg" { };
-
-    /// Build a
-    /// [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)
-    /// element.
-    path <> "http://www.w3.org/2000/svg" {
-
-    };
-
-    /// Build a
-    /// [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)
-    /// element.
-    circle <>  "http://www.w3.org/2000/svg" {
-
-    };
-
-    /// Build a
-    /// [`<ellipse>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse)
-    /// element.
-    ellipse <> "http://www.w3.org/2000/svg" {
-
-    };
-
-    /// Build a
-    /// [`<line>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line)
-    /// element.
-    line <> "http://www.w3.org/2000/svg" {
-
-    };
-
-    /// Build a
-    /// [`<polygon>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon)
-    /// element.
-    polygon <> "http://www.w3.org/2000/svg" {
-
-    };
-
-    /// Build a
-    /// [`<polyline>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline)
-    /// element.
-    polyline <> "http://www.w3.org/2000/svg" {
-
-    };
-
-    /// Build a
-    /// [`<rect>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect)
-    /// element.
-    rect <> "http://www.w3.org/2000/svg" {
-
-    };
-
-    /// Build a
-    /// [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image)
-    /// element.
-    image <> "http://www.w3.org/2000/svg" {
-
-    };
-
-}
-
-/*
-Ideal format:
-    /// Build a
-    /// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
-    /// element.
-    ///
-    /// # About
-    /// - The HTML `<h1>` element is found within the `<body>` tag.
-    /// - Headings can range from `<h1>` to `<h6>`.
-    /// - The most important heading is `<h1>` and the least important heading is `<h6>`.
-    /// - The `<h1>` heading is the first heading in the document.
-    /// - The `<h1>` heading is usually a large bolded font.
-    ///
-    /// # Usage
-    ///
-    /// ```
-    /// html!(<h1> A header element </h1>)
-    /// rsx!(h1 { "A header element" })
-    /// LazyNodes::new(|f| f.el(h1).children([f.text("A header element")]).finish())
-    /// ```
+mod elements;
+mod events;
+mod global_attributes;
 
 
-Full List:
----------
-base
-head
-link
-meta
-style
-title
-body
-address
-article
-aside
-footer
-header
-h1
-h1
-h2
-h2
-h3
-h4
-h5
-h6
-main
-nav
-section
-blockquote
-dd
-div
-dl
-dt
-figcaption
-figure
-hr
-li
-ol
-p
-pre
-ul
-a
-abbr
-b
-bdi
-bdo
-br
-cite
-code
-data
-dfn
-em
-i
-kbd
-mark
-q
-rp
-rt
-ruby
-s
-samp
-small
-span
-strong
-sub
-sup
-time
-u
-var
-wbr
-area
-audio
-img
-map
-track
-video
-embed
-iframe
-object
-param
-picture
-source
-canvas
-noscript
-script
-del
-ins
-caption
-col
-colgroup
-table
-tbody
-td
-tfoot
-th
-thead
-tr
-button
-datalist
-fieldset
-form
-input
-label
-legend
-meter
-optgroup
-option
-output
-progress
-select
-textarea
-details
-summary
-slot
-template
-*/
+pub use elements::*;
+pub use events::*;
+pub use global_attributes::*;

+ 5 - 8
src/lib.rs

@@ -170,8 +170,11 @@
 #[cfg(feature = "core")]
 #[cfg(feature = "core")]
 pub use dioxus_core as core;
 pub use dioxus_core as core;
 
 
-#[cfg(feature = "core")]
-pub use dioxus_core::events;
+#[cfg(feature = "hooks")]
+pub use dioxus_hooks as hooks;
+
+#[cfg(feature = "ssr")]
+pub use dioxus_ssr as ssr;
 
 
 #[cfg(feature = "web")]
 #[cfg(feature = "web")]
 pub use dioxus_web as web;
 pub use dioxus_web as web;
@@ -179,12 +182,6 @@ pub use dioxus_web as web;
 #[cfg(feature = "mobile")]
 #[cfg(feature = "mobile")]
 pub use dioxus_mobile as mobile;
 pub use dioxus_mobile as mobile;
 
 
-#[cfg(feature = "ssr")]
-pub use dioxus_ssr as ssr;
-
-#[cfg(feature = "hooks")]
-pub use dioxus_hooks as hooks;
-
 #[cfg(feature = "desktop")]
 #[cfg(feature = "desktop")]
 pub use dioxus_desktop as desktop;
 pub use dioxus_desktop as desktop;