瀏覽代碼

Merge pull request #11 from jkelleyrtp/jk/constify

change vnode structure
Jonathan Kelley 4 年之前
父節點
當前提交
211023b92d
共有 41 個文件被更改,包括 1958 次插入1458 次删除
  1. 4 4
      Cargo.toml
  2. 1 5
      README.md
  3. 1 1
      examples/rsx_usage.rs
  4. 4 1
      examples/testbed.rs
  5. 21 0
      notes/ARCHITECTURE.md
  6. 1 1
      packages/core-macro/src/rsx/component.rs
  7. 37 34
      packages/core-macro/src/rsx/element.rs
  8. 4 0
      packages/core-macro/src/rsx/mod.rs
  9. 1 1
      packages/core/.vscode/settings.json
  10. 1 1
      packages/core/Cargo.toml
  11. 16 16
      packages/core/examples/alternative.rs
  12. 15 3
      packages/core/examples/async.rs
  13. 2 2
      packages/core/examples/borrowed.rs
  14. 1 1
      packages/core/examples/fragment.rs
  15. 1 1
      packages/core/examples/html.rs
  16. 4 4
      packages/core/src/arena.rs
  17. 5 18
      packages/core/src/bumpframe.rs
  18. 1 2
      packages/core/src/component.rs
  19. 105 37
      packages/core/src/context.rs
  20. 95 95
      packages/core/src/diff.rs
  21. 1 2
      packages/core/src/events.rs
  22. 32 39
      packages/core/src/lib.rs
  23. 337 378
      packages/core/src/nodes.rs
  24. 0 265
      packages/core/src/oldbuilder.rs
  25. 27 12
      packages/core/src/scope.rs
  26. 0 116
      packages/core/src/styles.rs
  27. 62 57
      packages/core/src/tasks.rs
  28. 17 15
      packages/core/src/util.rs
  29. 22 8
      packages/core/src/virtual_dom.rs
  30. 1 1
      packages/core/tests/integration.rs
  31. 7 2
      packages/hooks/src/usestate.rs
  32. 86 1
      packages/html/README.md
  33. 922 113
      packages/html/src/lib.rs
  34. 8 7
      packages/ssr/src/lib.rs
  35. 11 7
      packages/web/Cargo.toml
  36. 35 18
      packages/web/examples/async_web.rs
  37. 47 34
      packages/web/src/lib.rs
  38. 9 4
      packages/web/src/new.rs
  39. 4 2
      packages/webview/examples/test.rs
  40. 9 150
      packages/webview/src/lib.rs
  41. 1 0
      src/lib.rs

+ 4 - 4
Cargo.toml

@@ -20,14 +20,14 @@ dioxus-ssr = { path = "./packages/ssr", optional = true }
 [features]
 [features]
 default = [
 default = [
     "core",
     "core",
-    "atoms",
     "macro",
     "macro",
-    "ssr",
     "hooks",
     "hooks",
-    "router",
     "html",
     "html",
+    "ssr",
     "web",
     "web",
     "desktop",
     "desktop",
+    # "atoms",
+    # "router",
 ]
 ]
 atoms = []
 atoms = []
 macro = ["dioxus-core-macro"]
 macro = ["dioxus-core-macro"]
@@ -63,8 +63,8 @@ members = [
     "packages/html",
     "packages/html",
     "packages/hooks",
     "packages/hooks",
     "packages/web",
     "packages/web",
-    "packages/webview",
     "packages/ssr",
     "packages/ssr",
+    "packages/webview",
     # "packages/atoms",
     # "packages/atoms",
     # "packages/docsite",
     # "packages/docsite",
 ]
 ]

+ 1 - 5
README.md

@@ -41,10 +41,6 @@
     <a href="https://docs.rs/async-imap">
     <a href="https://docs.rs/async-imap">
       Examples
       Examples
     </a>
     </a>
-    <span> | </span>
-    <a href="https://github.com/async-email/async-imap/releases">
-      Releases
-    </a>
   </h3>
   </h3>
 </div>
 </div>
 
 
@@ -170,10 +166,10 @@ Dioxus is heavily inspired by React, but we want your transition to feel like an
 | Suspense                | 🛠      | ✅     | schedule future render from future/promise                  |
 | Suspense                | 🛠      | ✅     | schedule future render from future/promise                  |
 | Cooperative Scheduling  | 🛠      | ✅     | Prioritize important events over non-important events       |
 | Cooperative Scheduling  | 🛠      | ✅     | Prioritize important events over non-important events       |
 | Fine-grained reactivity | 🛠      | ❓     | Skip diffing for fine-grain updates                         |
 | Fine-grained reactivity | 🛠      | ❓     | Skip diffing for fine-grain updates                         |
-| Compile-time correct    | ✅      | ❓     | Throw errors on invalid template layouts                    |
 | Runs natively           | ✅      | ❓     | runs as a portable binary w/o a runtime (Node)              |
 | Runs natively           | ✅      | ❓     | runs as a portable binary w/o a runtime (Node)              |
 | 1st class global state  | ✅      | ❓     | redux/recoil/mobx on top of context                         |
 | 1st class global state  | ✅      | ❓     | redux/recoil/mobx on top of context                         |
 | Subtree Memoization     | ✅      | ❓     | skip diffing static element subtrees                        |
 | Subtree Memoization     | ✅      | ❓     | skip diffing static element subtrees                        |
+| Compile-time correct    | ✅      | ❓     | Throw errors on invalid template layouts                    |
 | Heuristic Engine        | 🛠      | ❓     | track component memory usage to minimize future allocations |
 | Heuristic Engine        | 🛠      | ❓     | track component memory usage to minimize future allocations |
 | NodeRef                 | 🛠      | ✅     | gain direct access to nodes [1]                             |
 | NodeRef                 | 🛠      | ✅     | gain direct access to nodes [1]                             |
 
 

+ 1 - 1
examples/rsx_usage.rs

@@ -89,7 +89,7 @@ static Example: FC<()> = |cx| {
 
 
             // Expressions can be used in element position too:
             // Expressions can be used in element position too:
             {rsx!(p { "More templating!" })}
             {rsx!(p { "More templating!" })}
-            {html!(<p>"Even HTML templating!!"</p>)}
+            // {html!(<p>"Even HTML templating!!"</p>)}
 
 
             // Iterators
             // Iterators
             {(0..10).map(|i| rsx!(li { "{i}" }))}
             {(0..10).map(|i| rsx!(li { "{i}" }))}

+ 4 - 1
examples/testbed.rs

@@ -2,7 +2,7 @@ use std::cell::Cell;
 
 
 use dioxus::prelude::*;
 use dioxus::prelude::*;
 use dioxus_core::{
 use dioxus_core::{
-    nodes::{NodeKey, VElement, VText},
+    nodes::{VElement, VText},
     RealDomNode,
     RealDomNode,
 };
 };
 
 
@@ -23,6 +23,9 @@ const Example: FC<()> = |cx| {
             Fragment {
             Fragment {
                 Fragment {
                 Fragment {
                     "h1"
                     "h1"
+                    div {
+
+                    }
                 }
                 }
                 "h2"
                 "h2"
             }
             }

+ 21 - 0
notes/ARCHITECTURE.md

@@ -50,3 +50,24 @@ div {
 ## Subtree memoization
 ## Subtree memoization
 
 
 The rsx! macro needs to be *really* smart. If it detects that no dynamics are pumped into the macro, then it opts to use the "const" flavors of the element build functions we know and love. This has to be done at build time rather than runtime since components may return basically anything. Using the const flavor enables is_static which encourages Dioxus to do a ptr compare instead of a value compare to short circuit through the diffing. Due to const folding in Rust, entire subtrees can be ruled out at compile time.
 The rsx! macro needs to be *really* smart. If it detects that no dynamics are pumped into the macro, then it opts to use the "const" flavors of the element build functions we know and love. This has to be done at build time rather than runtime since components may return basically anything. Using the const flavor enables is_static which encourages Dioxus to do a ptr compare instead of a value compare to short circuit through the diffing. Due to const folding in Rust, entire subtrees can be ruled out at compile time.
+
+It would be interesting to fix the issue of dynamic subtrees by hashing each structure (or just the const structures) or the macro call itself. That way, each call gets its own identifier and we can make sure that two unique structures have different IDs and aren't just opaque to dioxus.
+
+```rust
+let s1 = LazyNodes::new("1", move |_| {
+    if rand() {
+        f.element()
+    } else {
+        f.element()
+    }
+});
+let s2 = LazyNodes::new("1", move |f| {
+    if rand() {
+        f.element()
+    } else {
+        f.element()
+    }
+});
+// produces the same ID with different structures
+// perhaps just make this
+```

+ 1 - 1
packages/core-macro/src/rsx/component.rs

@@ -188,7 +188,7 @@ impl ToTokens for Component {
                 #name,
                 #name,
                 #builder,
                 #builder,
                 #key_token,
                 #key_token,
-                #children
+                __cx.bump().alloc(#children)
             )
             )
         })
         })
     }
     }

+ 37 - 34
packages/core-macro/src/rsx/element.rs

@@ -23,37 +23,18 @@ pub struct Element {
 impl ToTokens for Element {
 impl ToTokens for Element {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let name = &self.name;
         let name = &self.name;
+        let attr = &self.attributes;
+        let childs = &self.children;
+        let listeners = &self.listeners;
 
 
         tokens.append_all(quote! {
         tokens.append_all(quote! {
-            __cx.element(dioxus_elements::#name)
-        });
-
-        // By gating these methods, we can keep the output of `cargo expand` readable.
-        // We also prevent the issue where zero-sized arrays fail to have propery type inference.
-
-        if self.attributes.len() > 0 {
-            let attr = &self.attributes;
-            tokens.append_all(quote! {
-                .attributes([ #(#attr),* ])
-            })
-        }
-
-        if self.children.len() > 0 {
-            let childs = &self.children;
-            tokens.append_all(quote! {
-                .children([ #(#childs),* ])
-            });
-        }
-
-        if self.listeners.len() > 0 {
-            let listeners = &self.listeners;
-            tokens.append_all(quote! {
-                .listeners([ #(#listeners),* ])
-            });
-        }
-
-        tokens.append_all(quote! {
-            .finish()
+            __cx.element(
+                dioxus_elements::#name,
+                __cx.bump().alloc([ #(#listeners),* ]),
+                __cx.bump().alloc([ #(#attr),* ]),
+                __cx.bump().alloc([ #(#childs),* ]),
+                None,
+            )
         });
         });
     }
     }
 }
 }
@@ -83,7 +64,13 @@ impl Parse for Element {
             }
             }
 
 
             if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
             if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
-                parse_element_body(&content, &mut attributes, &mut listeners, &mut key)?;
+                parse_element_body(
+                    &content,
+                    &mut attributes,
+                    &mut listeners,
+                    &mut key,
+                    name.clone(),
+                )?;
             } else {
             } else {
                 children.push(content.parse::<Node>()?);
                 children.push(content.parse::<Node>()?);
             }
             }
@@ -110,6 +97,7 @@ impl Parse for Element {
 /// Parse a VElement's Attributes
 /// Parse a VElement's Attributes
 /// =======================================
 /// =======================================
 struct ElementAttr {
 struct ElementAttr {
+    element_name: Ident,
     name: Ident,
     name: Ident,
     value: AttrType,
     value: AttrType,
     namespace: Option<String>,
     namespace: Option<String>,
@@ -130,6 +118,7 @@ fn parse_element_body(
     attrs: &mut Vec<ElementAttr>,
     attrs: &mut Vec<ElementAttr>,
     listeners: &mut Vec<ElementAttr>,
     listeners: &mut Vec<ElementAttr>,
     key: &mut Option<AttrType>,
     key: &mut Option<AttrType>,
+    element_name: Ident,
 ) -> Result<()> {
 ) -> Result<()> {
     let mut name = Ident::parse_any(stream)?;
     let mut name = Ident::parse_any(stream)?;
     let name_str = name.to_string();
     let name_str = name.to_string();
@@ -159,6 +148,7 @@ fn parse_element_body(
             name,
             name,
             value: ty,
             value: ty,
             namespace: None,
             namespace: None,
+            element_name: element_name.clone(),
         });
         });
         return Ok(());
         return Ok(());
     }
     }
@@ -186,6 +176,7 @@ fn parse_element_body(
                     name,
                     name,
                     value: ty,
                     value: ty,
                     namespace: Some("style".to_string()),
                     namespace: Some("style".to_string()),
+                    element_name: element_name.clone(),
                 });
                 });
             }
             }
 
 
@@ -227,13 +218,15 @@ fn parse_element_body(
         name,
         name,
         value: ty,
         value: ty,
         namespace: None,
         namespace: None,
+        element_name,
     });
     });
     Ok(())
     Ok(())
 }
 }
 
 
 impl ToTokens for ElementAttr {
 impl ToTokens for ElementAttr {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
-        let name = self.name.to_string();
+        let el_name = &self.element_name;
+        let name_str = self.name.to_string();
         let nameident = &self.name;
         let nameident = &self.name;
 
 
         let namespace = match &self.namespace {
         let namespace = match &self.namespace {
@@ -243,12 +236,22 @@ impl ToTokens for ElementAttr {
 
 
         match &self.value {
         match &self.value {
             AttrType::BumpText(value) => tokens.append_all(quote! {
             AttrType::BumpText(value) => tokens.append_all(quote! {
-                __cx.attr(#name, format_args_f!(#value), #namespace)
+                dioxus_elements::#el_name.#nameident(__cx, format_args_f!(#value))
             }),
             }),
-
+            // __cx.attr(#name, format_args_f!(#value), #namespace, false)
+            //
+            // AttrType::BumpText(value) => tokens.append_all(quote! {
+            //     __cx.attr(#name, format_args_f!(#value), #namespace, false)
+            // }),
             AttrType::FieldTokens(exp) => tokens.append_all(quote! {
             AttrType::FieldTokens(exp) => tokens.append_all(quote! {
-                __cx.attr(#name, #exp, #namespace)
+                dioxus_elements::#el_name.#nameident(__cx, #exp)
             }),
             }),
+            // __cx.attr(#name_str, #exp, #namespace, false)
+
+            // AttrType::FieldTokens(exp) => tokens.append_all(quote! {
+            //     dioxus_elements::#el_name.#nameident(__cx, format_args_f!(#value))
+            //     __cx.attr(#name_str, #exp, #namespace, false)
+            // }),
 
 
             // todo: move event handlers on to the elements or onto the nodefactory
             // todo: move event handlers on to the elements or onto the nodefactory
             AttrType::Event(event) => tokens.append_all(quote! {
             AttrType::Event(event) => tokens.append_all(quote! {

+ 4 - 0
packages/core-macro/src/rsx/mod.rs

@@ -97,12 +97,16 @@ impl ToTokens for RsxRender {
             // The `in cx` pattern allows directly rendering
             // The `in cx` pattern allows directly rendering
             Some(ident) => out_tokens.append_all(quote! {
             Some(ident) => out_tokens.append_all(quote! {
                 #ident.render(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
                 #ident.render(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
+                    use dioxus_elements::GlobalAttributes;
+
                     #inner
                     #inner
                 }))
                 }))
             }),
             }),
             // Otherwise we just build the LazyNode wrapper
             // Otherwise we just build the LazyNode wrapper
             None => out_tokens.append_all(quote! {
             None => out_tokens.append_all(quote! {
                 dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
                 dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
+                    use dioxus_elements::GlobalAttributes;
+
                     #inner
                     #inner
                  })
                  })
             }),
             }),

+ 1 - 1
packages/core/.vscode/settings.json

@@ -1,3 +1,3 @@
 {
 {
-    "rust-analyzer.inlayHints.enable": false
+    "rust-analyzer.inlayHints.enable": true
 }
 }

+ 1 - 1
packages/core/Cargo.toml

@@ -41,5 +41,5 @@ smallvec = "1.6.1"
 
 
 
 
 [features]
 [features]
-default = []
+default = ["serialize"]
 serialize = ["slotmap/serde", "serde"]
 serialize = ["slotmap/serde", "serde"]

+ 16 - 16
packages/core/examples/alternative.rs

@@ -1,20 +1,20 @@
 fn main() {}
 fn main() {}
 
 
-use dioxus::*;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
+// use dioxus::*;
+// use dioxus_core as dioxus;
+// use dioxus_core::prelude::*;
 
 
-static Example: FC<()> = |cx| {
-    let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
+// static Example: FC<()> = |cx| {
+//     let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
 
 
-    cx.render(LazyNodes::new(move |cx| {
-        let bump = cx.bump();
-        cx.raw_element("div")
-            .children([
-                cx.text(format_args!("hello")),
-                cx.text(format_args!("hello")),
-                cx.fragment_from_iter(list),
-            ])
-            .finish()
-    }))
-};
+//     cx.render(LazyNodes::new(move |cx| {
+//         let bump = cx.bump();
+//         cx.raw_element("div")
+//             .children([
+//                 cx.text(format_args!("hello")),
+//                 cx.text(format_args!("hello")),
+//                 cx.fragment_from_iter(list),
+//             ])
+//             .finish()
+//     }))
+// };

+ 15 - 3
packages/core/examples/async.rs

@@ -1,13 +1,13 @@
-use std::pin::Pin;
+
 
 
 use dioxus_core::prelude::*;
 use dioxus_core::prelude::*;
-use std::future::Future;
+
 
 
 fn main() {}
 fn main() {}
 
 
 const App: FC<()> = |cx| {
 const App: FC<()> = |cx| {
     // create a new future
     // create a new future
-    let mut fut = cx.use_hook(
+    let _fut = cx.use_hook(
         || {
         || {
             //
             //
             async { loop {} }
             async { loop {} }
@@ -22,3 +22,15 @@ const App: FC<()> = |cx| {
 
 
     todo!()
     todo!()
 };
 };
+
+const Task: FC<()> = |cx| {
+    //
+
+    let _s = cx.use_task(|| async { "hello world".to_string() });
+
+    todo!()
+};
+
+fn use_mut<P, T>(_cx: Context<P>, _f: impl FnOnce() -> T) -> &mut T {
+    todo!()
+}

+ 2 - 2
packages/core/examples/borrowed.rs

@@ -7,7 +7,7 @@
 fn main() {}
 fn main() {}
 
 
 use dioxus_core::prelude::*;
 use dioxus_core::prelude::*;
-use dioxus_core::*;
+
 use std::rc::Rc;
 use std::rc::Rc;
 
 
 struct AppProps {
 struct AppProps {
@@ -61,7 +61,7 @@ fn ChildItem<'a>(cx: Context<'a, ChildProps>) -> VNode {
 }
 }
 
 
 impl PartialEq for ChildProps {
 impl PartialEq for ChildProps {
-    fn eq(&self, other: &Self) -> bool {
+    fn eq(&self, _other: &Self) -> bool {
         false
         false
     }
     }
 }
 }

+ 1 - 1
packages/core/examples/fragment.rs

@@ -1,4 +1,4 @@
-use dioxus_core::prelude::*;
+
 
 
 fn main() {
 fn main() {
 
 

+ 1 - 1
packages/core/examples/html.rs

@@ -68,7 +68,7 @@ impl BasicElement for div {
         todo!()
         todo!()
     }
     }
 
 
-    fn new(factory: Factory) -> Self {
+    fn new(_factory: Factory) -> Self {
         todo!()
         todo!()
     }
     }
 }
 }

+ 4 - 4
packages/core/src/arena.rs

@@ -59,8 +59,8 @@ impl SharedArena {
 
 
     pub fn with_scope<'b, O: 'static>(
     pub fn with_scope<'b, O: 'static>(
         &'b self,
         &'b self,
-        id: ScopeIdx,
-        f: impl FnOnce(&'b mut Scope) -> O,
+        _id: ScopeIdx,
+        _f: impl FnOnce(&'b mut Scope) -> O,
     ) -> Result<O> {
     ) -> Result<O> {
         todo!()
         todo!()
     }
     }
@@ -69,8 +69,8 @@ impl SharedArena {
     // this is useful for merging lifetimes
     // this is useful for merging lifetimes
     pub fn with_scope_vnode<'b>(
     pub fn with_scope_vnode<'b>(
         &self,
         &self,
-        id: ScopeIdx,
-        f: impl FnOnce(&mut Scope) -> &VNode<'b>,
+        _id: ScopeIdx,
+        _f: impl FnOnce(&mut Scope) -> &VNode<'b>,
     ) -> Result<&VNode<'b>> {
     ) -> Result<&VNode<'b>> {
         todo!()
         todo!()
     }
     }

+ 5 - 18
packages/core/src/bumpframe.rs

@@ -1,20 +1,7 @@
-use crate::hooklist::HookList;
-use crate::{arena::SharedArena, innerlude::*};
-use appendlist::AppendList;
+use crate::innerlude::*;
 use bumpalo::Bump;
 use bumpalo::Bump;
-use slotmap::DefaultKey;
-use slotmap::SlotMap;
-use std::marker::PhantomData;
-use std::{
-    any::{Any, TypeId},
-    cell::{Cell, RefCell},
-    collections::{HashMap, HashSet, VecDeque},
-    fmt::Debug,
-    future::Future,
-    ops::Deref,
-    pin::Pin,
-    rc::{Rc, Weak},
-};
+
+use std::cell::RefCell;
 pub struct ActiveFrame {
 pub struct ActiveFrame {
     // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
     // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
     pub generation: RefCell<usize>,
     pub generation: RefCell<usize>,
@@ -33,11 +20,11 @@ impl ActiveFrame {
         Self::from_frames(
         Self::from_frames(
             BumpFrame {
             BumpFrame {
                 bump: Bump::new(),
                 bump: Bump::new(),
-                head_node: VNode::static_text(""),
+                head_node: NodeFactory::static_text(""),
             },
             },
             BumpFrame {
             BumpFrame {
                 bump: Bump::new(),
                 bump: Bump::new(),
-                head_node: VNode::static_text(""),
+                head_node: NodeFactory::static_text(""),
             },
             },
         )
         )
     }
     }

+ 1 - 2
packages/core/src/component.rs

@@ -5,7 +5,7 @@
 //! if the type suppports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
 //! if the type suppports 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::FC;
+use crate::innerlude::{Context, LazyNodes, VNode, FC};
 
 
 pub trait Properties: Sized {
 pub trait Properties: Sized {
     type Builder;
     type Builder;
@@ -50,7 +50,6 @@ pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
 ///
 ///
 /// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
 /// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
 /// Try to avoid nesting fragments if you can. Infinitely nested Fragments *will* cause diffing to crash.
 /// Try to avoid nesting fragments if you can. Infinitely nested Fragments *will* cause diffing to crash.
-use crate::prelude::*;
 #[allow(non_upper_case_globals, non_snake_case)]
 #[allow(non_upper_case_globals, non_snake_case)]
 pub fn Fragment<'a>(cx: Context<'a, ()>) -> VNode<'a> {
 pub fn Fragment<'a>(cx: Context<'a, ()>) -> VNode<'a> {
     cx.render(LazyNodes::new(move |f| f.fragment_from_iter(cx.children())))
     cx.render(LazyNodes::new(move |f| f.fragment_from_iter(cx.children())))

+ 105 - 37
packages/core/src/context.rs

@@ -1,15 +1,12 @@
-use crate::hooklist::HookList;
-use crate::{arena::SharedArena, innerlude::*};
-use appendlist::AppendList;
-use bumpalo::Bump;
-use slotmap::DefaultKey;
-use slotmap::SlotMap;
+use crate::innerlude::*;
+
+use futures_util::FutureExt;
+
 use std::marker::PhantomData;
 use std::marker::PhantomData;
+
 use std::{
 use std::{
-    any::{Any, TypeId},
-    cell::{Cell, RefCell},
-    collections::{HashMap, HashSet, VecDeque},
-    fmt::Debug,
+    any::TypeId,
+    cell::RefCell,
     future::Future,
     future::Future,
     ops::Deref,
     ops::Deref,
     pin::Pin,
     pin::Pin,
@@ -39,9 +36,8 @@ use std::{
 pub struct Context<'src, T> {
 pub struct Context<'src, T> {
     pub props: &'src T,
     pub props: &'src T,
     pub scope: &'src Scope,
     pub scope: &'src Scope,
-    pub tasks: &'src RefCell<Vec<&'src mut PinnedTask>>,
 }
 }
-pub type PinnedTask = Pin<Box<dyn Future<Output = ()>>>;
+// pub type PinnedTask = Pin<Box<dyn Future<Output = ()>>>;
 
 
 impl<'src, T> Copy for Context<'src, T> {}
 impl<'src, T> Copy for Context<'src, T> {}
 impl<'src, T> Clone for Context<'src, T> {
 impl<'src, T> Clone for Context<'src, T> {
@@ -49,7 +45,6 @@ impl<'src, T> Clone for Context<'src, T> {
         Self {
         Self {
             props: self.props,
             props: self.props,
             scope: self.scope,
             scope: self.scope,
-            tasks: self.tasks,
         }
         }
     }
     }
 }
 }
@@ -134,7 +129,7 @@ impl<'src, P> Context<'src, P> {
         self,
         self,
         initializer: Init,
         initializer: Init,
         runner: Run,
         runner: Run,
-        cleanup: Cleanup,
+        _cleanup: Cleanup,
     ) -> Output
     ) -> Output
     where
     where
         State: 'static,
         State: 'static,
@@ -263,31 +258,13 @@ Any function prefixed with "use" should not be called conditionally.
         )
         )
     }
     }
 
 
-    pub fn suspend<Output: 'src, Fut: FnOnce(SuspendedContext, Output) -> VNode<'src> + 'src>(
-        &'src self,
-        fut: &'src mut Pin<Box<dyn Future<Output = Output> + 'static>>,
-        callback: Fut,
-    ) -> VNode<'src> {
-        use futures_util::FutureExt;
-        match fut.now_or_never() {
-            Some(out) => {
-                let suspended_cx = SuspendedContext {};
-                let nodes = callback(suspended_cx, out);
-                return nodes;
-            }
-            None => {
-                // we need to register this task
-                VNode::Suspended {
-                    real: Cell::new(RealDomNode::empty()),
-                }
-            }
-        }
-    }
-
     /// `submit_task` will submit the future to be polled.
     /// `submit_task` will submit the future to be polled.
     ///
     ///
     /// This is useful when you have some async task that needs to be progressed.
     /// This is useful when you have some async task that needs to be progressed.
     ///
     ///
+    /// This method takes ownership over the task you've provided, and must return (). This means any work that needs to
+    /// happen must occur within the future or scheduled for after the future completes (through schedule_update )
+    ///
     /// ## Explanation
     /// ## Explanation
     /// Dioxus will step its internal event loop if the future returns if the future completes while waiting.
     /// Dioxus will step its internal event loop if the future returns if the future completes while waiting.
     ///
     ///
@@ -298,11 +275,102 @@ Any function prefixed with "use" should not be called conditionally.
     ///
     ///
     ///
     ///
     ///
     ///
-    pub fn submit_task(&self, task: &'src mut PinnedTask) -> TaskHandle {
-        self.tasks.borrow_mut().push(task);
+    pub fn submit_task(&self, task: FiberTask) -> TaskHandle {
+        let r = (self.scope.task_submitter)(task);
+        // self.scope.submit_task(task);
+        // let r = task.then(|f| async {
+        //     //
+        // });
+        // self.use_hook(|| Box::new(r), |_| {}, |_| {});
+        // *task = task.then(|f| async {
+        //     //
+        //     t
+        //     // ()
+        // });
+        // let new_fut = task.then(|f| async {
+        //     //
+        //     ()
+        // });
+        // self.tasks.borrow_mut().push(new_fut);
 
 
         TaskHandle { _p: PhantomData {} }
         TaskHandle { _p: PhantomData {} }
     }
     }
+
+    /// Awaits the given task, forcing the component to re-render when the value is ready.
+    ///
+    ///
+    pub fn use_task<Out, Fut, Init>(&self, task_initializer: Init) -> &mut Option<Out>
+    where
+        Out: 'static,
+        Fut: Future<Output = Out>,
+        Fut: 'static,
+        Init: FnOnce() -> Fut + 'src,
+    {
+        struct TaskHook<T> {
+            task_dump: Rc<RefCell<Option<T>>>,
+            value: Option<T>,
+        }
+
+        // whenever the task is complete, save it into th
+        self.use_hook(
+            move || {
+                let task_fut = task_initializer();
+
+                let task_dump = Rc::new(RefCell::new(None));
+
+                let slot = task_dump.clone();
+                let update = self.schedule_update();
+                let originator = self.scope.arena_idx.clone();
+
+                self.submit_task(Box::pin(task_fut.then(move |output| async move {
+                    *slot.as_ref().borrow_mut() = Some(output);
+                    update();
+                    EventTrigger {
+                        event: VirtualEvent::FiberEvent,
+                        originator,
+                        priority: EventPriority::Low,
+                        real_node_id: None,
+                    }
+                })));
+
+                TaskHook {
+                    task_dump,
+                    value: None,
+                }
+            },
+            |hook| {
+                if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() {
+                    hook.value = Some(val);
+                }
+                &mut hook.value
+            },
+            |_| {},
+        )
+    }
+
+    /// Asynchronously render new nodes once the given future has completed.
+    ///
+    /// # Easda
+    ///
+    ///
+    ///
+    ///
+    /// # Example
+    ///
+    ///
+    pub fn use_suspense<Out, Fut: 'static>(
+        &'src self,
+        _task_initializer: impl FnOnce() -> Fut,
+        _callback: impl FnOnce(SuspendedContext, Out) -> VNode<'src> + 'src,
+    ) -> VNode<'src>
+    where
+        Out: 'src,
+        Fut: Future<Output = Out>,
+    {
+        // self.use_hook(|| , runner, cleanup)
+
+        todo!()
+    }
 }
 }
 
 
 pub struct TaskHandle<'src> {
 pub struct TaskHandle<'src> {

+ 95 - 95
packages/core/src/diff.rs

@@ -49,7 +49,7 @@
 //!  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
 //!  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
 
 
 use crate::{arena::SharedArena, innerlude::*, tasks::TaskQueue};
 use crate::{arena::SharedArena, innerlude::*, tasks::TaskQueue};
-use fxhash::{FxHashMap, FxHashSet};
+use fxhash::FxHashSet;
 
 
 use std::any::Any;
 use std::any::Any;
 
 
@@ -107,18 +107,6 @@ pub trait RealDom<'a> {
     fn raw_node_as_any_mut(&self) -> &mut dyn Any;
     fn raw_node_as_any_mut(&self) -> &mut dyn Any;
 }
 }
 
 
-/// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
-/// diffing trees of components. This means we can "re-enter" a subtree of a component by queuing a "NeedToDiff" event.
-///
-/// By re-entering via NodeDiff, we can connect disparate edits together into a single EditList. This batching of edits
-/// leads to very fast re-renders (all done in a single animation frame).
-///
-/// It also means diffing two trees is only ever complex as diffing a single smaller tree, and then re-entering at a
-/// different cursor position.
-///
-/// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
-/// that were modified by the eventtrigger. This prevents doubly evaluating components if they were both updated via
-/// subscriptions and props changes.
 pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
 pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
     pub dom: &'real mut Dom,
     pub dom: &'real mut Dom,
     pub components: &'bump SharedArena,
     pub components: &'bump SharedArena,
@@ -158,50 +146,48 @@ where
     //
     //
     // each function call assumes the stack is fresh (empty).
     // each function call assumes the stack is fresh (empty).
     pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
     pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
-        match (old_node, new_node) {
+        match (&old_node.kind, &new_node.kind) {
             // Handle the "sane" cases first.
             // Handle the "sane" cases first.
             // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
             // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
             // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
             // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
-            (VNode::Text(old), VNode::Text(new)) => {
-                if old.is_static {
-                    log::debug!("encountered static text node: {:?}", old.text);
-                }
-
+            (VNodeKind::Text(old), VNodeKind::Text(new)) => {
+                let root = old_node.dom_id.get();
                 if old.text != new.text {
                 if old.text != new.text {
-                    self.dom.push(old.dom_id.get());
+                    self.dom.push(root);
                     log::debug!("Text has changed {}, {}", old.text, new.text);
                     log::debug!("Text has changed {}, {}", old.text, new.text);
                     self.dom.set_text(new.text);
                     self.dom.set_text(new.text);
                     self.dom.pop();
                     self.dom.pop();
                 }
                 }
 
 
-                new.dom_id.set(old.dom_id.get());
+                new_node.dom_id.set(root);
             }
             }
 
 
-            (VNode::Element(old), VNode::Element(new)) => {
+            (VNodeKind::Element(old), VNodeKind::Element(new)) => {
                 // If the element type is completely different, the element needs to be re-rendered completely
                 // If the element type is completely different, the element needs to be re-rendered completely
                 // This is an optimization React makes due to how users structure their code
                 // This is an optimization React makes due to how users structure their code
                 //
                 //
                 // In Dioxus, this is less likely to occur unless through a fragment
                 // In Dioxus, this is less likely to occur unless through a fragment
+                let root = old_node.dom_id.get();
                 if new.tag_name != old.tag_name || new.namespace != old.namespace {
                 if new.tag_name != old.tag_name || new.namespace != old.namespace {
-                    self.dom.push(old.dom_id.get());
+                    self.dom.push(root);
                     let meta = self.create(new_node);
                     let meta = self.create(new_node);
                     self.dom.replace_with(meta.added_to_stack);
                     self.dom.replace_with(meta.added_to_stack);
                     self.dom.pop();
                     self.dom.pop();
                     return;
                     return;
                 }
                 }
 
 
-                let oldid = old.dom_id.get();
-                new.dom_id.set(oldid);
+                new_node.dom_id.set(root);
 
 
                 // push it just in case
                 // push it just in case
-                self.dom.push(oldid);
+                // TODO: remove this - it clogs up things and is inefficient
+                self.dom.push(root);
                 self.diff_listeners(old.listeners, new.listeners);
                 self.diff_listeners(old.listeners, new.listeners);
                 self.diff_attr(old.attributes, new.attributes, new.namespace);
                 self.diff_attr(old.attributes, new.attributes, new.namespace);
                 self.diff_children(old.children, new.children);
                 self.diff_children(old.children, new.children);
                 self.dom.pop();
                 self.dom.pop();
             }
             }
 
 
-            (VNode::Component(old), VNode::Component(new)) => {
+            (VNodeKind::Component(old), VNodeKind::Component(new)) => {
                 log::warn!("diffing components? {:#?}", new.user_fc);
                 log::warn!("diffing components? {:#?}", new.user_fc);
                 if old.user_fc == new.user_fc {
                 if old.user_fc == new.user_fc {
                     // Make sure we're dealing with the same component (by function pointer)
                     // Make sure we're dealing with the same component (by function pointer)
@@ -257,7 +243,7 @@ where
                 }
                 }
             }
             }
 
 
-            (VNode::Fragment(old), VNode::Fragment(new)) => {
+            (VNodeKind::Fragment(old), VNodeKind::Fragment(new)) => {
                 // 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 {
@@ -278,15 +264,19 @@ where
             // We also walk the "real node" list to make sure all latent roots are claened up
             // We also walk the "real node" list to make sure all latent roots are claened up
             // This covers the case any time a fragment or component shows up with pretty much anything else
             // This covers the case any time a fragment or component shows up with pretty much anything else
             (
             (
-                VNode::Component(_) | VNode::Fragment(_) | VNode::Text(_) | VNode::Element(_),
-                VNode::Component(_) | VNode::Fragment(_) | VNode::Text(_) | VNode::Element(_),
+                VNodeKind::Component(_)
+                | VNodeKind::Fragment(_)
+                | VNodeKind::Text(_)
+                | VNodeKind::Element(_),
+                VNodeKind::Component(_)
+                | VNodeKind::Fragment(_)
+                | VNodeKind::Text(_)
+                | VNodeKind::Element(_),
             ) => {
             ) => {
                 // Choose the node to use as the placeholder for replacewith
                 // Choose the node to use as the placeholder for replacewith
-                let back_node = match old_node {
+                let back_node = match old_node.kind {
                     // We special case these two types to avoid allocating the small-vecs
                     // We special case these two types to avoid allocating the small-vecs
-                    VNode::Element(_) | VNode::Text(_) => old_node
-                        .get_mounted_id(&self.components)
-                        .expect("Element and text always have a real node"),
+                    VNodeKind::Element(_) | VNodeKind::Text(_) => old_node.dom_id.get(),
 
 
                     _ => {
                     _ => {
                         let mut old_iter = RealChildIterator::new(old_node, &self.components);
                         let mut old_iter = RealChildIterator::new(old_node, &self.components);
@@ -314,17 +304,17 @@ where
             }
             }
 
 
             // TODO
             // TODO
-            (VNode::Suspended { .. }, _) => todo!(),
-            (_, VNode::Suspended { .. }) => todo!(),
+            (VNodeKind::Suspended { .. }, _) => todo!(),
+            (_, VNodeKind::Suspended { .. }) => todo!(),
         }
         }
     }
     }
 }
 }
 
 
 // When we create new nodes, we need to propagate some information back up the call chain.
 // When we create new nodes, we need to propagate some information back up the call chain.
 // This gives the caller some information on how to handle things like insertins, appending, and subtree discarding.
 // This gives the caller some information on how to handle things like insertins, appending, and subtree discarding.
-struct CreateMeta {
-    is_static: bool,
-    added_to_stack: u32,
+pub struct CreateMeta {
+    pub is_static: bool,
+    pub added_to_stack: u32,
 }
 }
 
 
 impl CreateMeta {
 impl CreateMeta {
@@ -349,15 +339,15 @@ where
     // When this function returns, the new node is on top of the change list stack:
     // When this function returns, the new node is on top of the change list stack:
     //
     //
     //     [... node]
     //     [... node]
-    fn create(&mut self, node: &'bump VNode<'bump>) -> CreateMeta {
+    pub fn create(&mut self, node: &'bump VNode<'bump>) -> CreateMeta {
         log::warn!("Creating node! ... {:#?}", node);
         log::warn!("Creating node! ... {:#?}", node);
-        match node {
-            VNode::Text(text) => {
+        match &node.kind {
+            VNodeKind::Text(text) => {
                 let real_id = self.dom.create_text_node(text.text);
                 let real_id = self.dom.create_text_node(text.text);
-                text.dom_id.set(real_id);
+                node.dom_id.set(real_id);
                 CreateMeta::new(text.is_static, 1)
                 CreateMeta::new(text.is_static, 1)
             }
             }
-            VNode::Element(el) => {
+            VNodeKind::Element(el) => {
                 // we have the potential to completely eliminate working on this node in the future(!)
                 // we have the potential to completely eliminate working on this node in the future(!)
                 //
                 //
                 // This can only be done if all of the elements properties (attrs, children, listeners, etc) are static
                 // This can only be done if all of the elements properties (attrs, children, listeners, etc) are static
@@ -366,14 +356,14 @@ where
                 let mut is_static: bool = true;
                 let mut is_static: bool = true;
 
 
                 let VElement {
                 let VElement {
-                    key,
                     tag_name,
                     tag_name,
                     listeners,
                     listeners,
                     attributes,
                     attributes,
                     children,
                     children,
                     namespace,
                     namespace,
-                    dom_id,
-                    is_static: el_is_static,
+                    static_attrs: _,
+                    static_children: _,
+                    static_listeners: _,
                 } = el;
                 } = el;
 
 
                 let real_id = if let Some(namespace) = namespace {
                 let real_id = if let Some(namespace) = namespace {
@@ -381,9 +371,10 @@ where
                 } else {
                 } else {
                     self.dom.create_element(tag_name, None)
                     self.dom.create_element(tag_name, None)
                 };
                 };
-                dom_id.set(real_id);
+                node.dom_id.set(real_id);
 
 
                 listeners.iter().enumerate().for_each(|(idx, listener)| {
                 listeners.iter().enumerate().for_each(|(idx, listener)| {
+                    log::info!("setting listener id to {:#?}", real_id);
                     listener.mounted_node.set(real_id);
                     listener.mounted_node.set(real_id);
                     self.dom
                     self.dom
                         .new_event_listener(listener.event, listener.scope, idx, real_id);
                         .new_event_listener(listener.event, listener.scope, idx, real_id);
@@ -408,9 +399,9 @@ where
                 //
                 //
                 // TODO move over
                 // TODO move over
                 // if children.len() == 1 {
                 // if children.len() == 1 {
-                //     if let VNode::Text(text) = &children[0] {
+                //     if let VNodeKind::Text(text) = &children[0].kind {
                 //         self.dom.set_text(text.text);
                 //         self.dom.set_text(text.text);
-                //         return;
+                //         return CreateMeta::new(is_static, 1);
                 //     }
                 //     }
                 // }
                 // }
 
 
@@ -422,17 +413,17 @@ where
                     self.dom.append_children(child_meta.added_to_stack);
                     self.dom.append_children(child_meta.added_to_stack);
                 }
                 }
 
 
-                if is_static {
-                    log::debug!("created a static node {:#?}", node);
-                } else {
-                    log::debug!("created a dynamic node {:#?}", node);
-                }
+                // if is_static {
+                //     log::debug!("created a static node {:#?}", node);
+                // } else {
+                //     log::debug!("created a dynamic node {:#?}", node);
+                // }
 
 
-                el_is_static.set(is_static);
+                // el_is_static.set(is_static);
                 CreateMeta::new(is_static, 1)
                 CreateMeta::new(is_static, 1)
             }
             }
 
 
-            VNode::Component(vcomponent) => {
+            VNodeKind::Component(vcomponent) => {
                 log::debug!("Mounting a new component");
                 log::debug!("Mounting a new component");
                 let caller = vcomponent.caller.clone();
                 let caller = vcomponent.caller.clone();
 
 
@@ -493,7 +484,7 @@ where
             // Fragments are the only nodes that can contain dynamic content (IE through curlies or iterators).
             // Fragments are the only nodes that can contain dynamic content (IE through curlies or iterators).
             // We can never ignore their contents, so the prescence of a fragment indicates that we need always diff them.
             // We can never ignore their contents, so the prescence of a fragment indicates that we need always diff them.
             // Fragments will just put all their nodes onto the stack after creation
             // Fragments will just put all their nodes onto the stack after creation
-            VNode::Fragment(frag) => {
+            VNodeKind::Fragment(frag) => {
                 let mut nodes_added = 0;
                 let mut nodes_added = 0;
                 for child in frag.children.iter().rev() {
                 for child in frag.children.iter().rev() {
                     // different types of nodes will generate different amounts on the stack
                     // different types of nodes will generate different amounts on the stack
@@ -508,9 +499,9 @@ where
                 CreateMeta::new(false, nodes_added)
                 CreateMeta::new(false, nodes_added)
             }
             }
 
 
-            VNode::Suspended { real } => {
+            VNodeKind::Suspended => {
                 let id = self.dom.create_placeholder();
                 let id = self.dom.create_placeholder();
-                real.set(id);
+                node.dom_id.set(id);
                 CreateMeta::new(false, 1)
                 CreateMeta::new(false, 1)
             }
             }
         }
         }
@@ -570,7 +561,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
             // if any characteristics changed, remove and then re-add
             // if any characteristics changed, remove and then re-add
 
 
             // if nothing changed, then just move on
             // if nothing changed, then just move on
-            let event_type = new_l.event;
+            let _event_type = new_l.event;
 
 
             for old_l in old {
             for old_l in old {
                 if new_l.event == old_l.event {
                 if new_l.event == old_l.event {
@@ -612,7 +603,6 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
         old: &'bump [Attribute<'bump>],
         old: &'bump [Attribute<'bump>],
         new: &'bump [Attribute<'bump>],
         new: &'bump [Attribute<'bump>],
         namespace: Option<&'bump str>,
         namespace: Option<&'bump str>,
-        // is_namespaced: bool,
     ) {
     ) {
         // Do O(n^2) passes to add/update and remove attributes, since
         // Do O(n^2) passes to add/update and remove attributes, since
         // there are almost always very few attributes.
         // there are almost always very few attributes.
@@ -620,7 +610,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
         // The "fast" path is when the list of attributes name is identical and in the same order
         // The "fast" path is when the list of attributes name is identical and in the same order
         // With the Rsx and Html macros, this will almost always be the case
         // With the Rsx and Html macros, this will almost always be the case
         'outer: for new_attr in new {
         'outer: for new_attr in new {
-            if new_attr.is_volatile() {
+            if new_attr.is_volatile {
                 // self.dom.commit_traversal();
                 // self.dom.commit_traversal();
                 self.dom
                 self.dom
                     .set_attribute(new_attr.name, new_attr.value, namespace);
                     .set_attribute(new_attr.name, new_attr.value, namespace);
@@ -675,13 +665,13 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
 
 
         if new.len() == 1 {
         if new.len() == 1 {
             match (&old.first(), &new[0]) {
             match (&old.first(), &new[0]) {
-                (Some(VNode::Text(old_vtext)), VNode::Text(new_vtext))
-                    if old_vtext.text == new_vtext.text =>
-                {
-                    // Don't take this fast path...
-                }
+                // (Some(VNodeKind::Text(old_vtext)), VNodeKind::Text(new_vtext))
+                //     if old_vtext.text == new_vtext.text =>
+                // {
+                //     // Don't take this fast path...
+                // }
 
 
-                // (_, VNode::Text(text)) => {
+                // (_, VNodeKind::Text(text)) => {
                 //     // self.dom.commit_traversal();
                 //     // self.dom.commit_traversal();
                 //     log::debug!("using optimized text set");
                 //     log::debug!("using optimized text set");
                 //     self.dom.set_text(text.text);
                 //     self.dom.set_text(text.text);
@@ -701,15 +691,15 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
             return;
             return;
         }
         }
 
 
-        let new_is_keyed = new[0].key().is_some();
-        let old_is_keyed = old[0].key().is_some();
+        let new_is_keyed = new[0].key.is_some();
+        let old_is_keyed = old[0].key.is_some();
 
 
         debug_assert!(
         debug_assert!(
-            new.iter().all(|n| n.key().is_some() == new_is_keyed),
+            new.iter().all(|n| n.key.is_some() == new_is_keyed),
             "all siblings must be keyed or all siblings must be non-keyed"
             "all siblings must be keyed or all siblings must be non-keyed"
         );
         );
         debug_assert!(
         debug_assert!(
-            old.iter().all(|o| o.key().is_some() == old_is_keyed),
+            old.iter().all(|o| o.key.is_some() == old_is_keyed),
             "all siblings must be keyed or all siblings must be non-keyed"
             "all siblings must be keyed or all siblings must be non-keyed"
         );
         );
 
 
@@ -754,7 +744,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
             let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
             let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
                 keys.clear();
                 keys.clear();
                 for child in children {
                 for child in children {
-                    let key = child.key();
+                    let key = child.key;
                     debug_assert!(
                     debug_assert!(
                         key.is_some(),
                         key.is_some(),
                         "if any sibling is keyed, all siblings must be keyed"
                         "if any sibling is keyed, all siblings must be keyed"
@@ -797,7 +787,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
             .iter()
             .iter()
             .rev()
             .rev()
             .zip(new[shared_prefix_count..].iter().rev())
             .zip(new[shared_prefix_count..].iter().rev())
-            .take_while(|&(old, new)| old.key() == new.key())
+            .take_while(|&(old, new)| old.key == new.key)
             .count();
             .count();
 
 
         let old_shared_suffix_start = old.len() - shared_suffix_count;
         let old_shared_suffix_start = old.len() - shared_suffix_count;
@@ -833,8 +823,8 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
     // Upon exit, the change list stack is the same.
     // Upon exit, the change list stack is the same.
     fn diff_keyed_prefix(
     fn diff_keyed_prefix(
         &self,
         &self,
-        old: &'bump [VNode<'bump>],
-        new: &'bump [VNode<'bump>],
+        _old: &'bump [VNode<'bump>],
+        _new: &'bump [VNode<'bump>],
     ) -> KeyedPrefixResult {
     ) -> KeyedPrefixResult {
         todo!()
         todo!()
         // self.dom.go_down();
         // self.dom.go_down();
@@ -922,11 +912,11 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
     // 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(
     fn diff_keyed_middle(
         &self,
         &self,
-        old: &[VNode<'bump>],
-        mut new: &[VNode<'bump>],
-        shared_prefix_count: usize,
-        shared_suffix_count: usize,
-        old_shared_suffix_start: usize,
+        _old: &[VNode<'bump>],
+        _new: &[VNode<'bump>],
+        _shared_prefix_count: usize,
+        _shared_suffix_count: usize,
+        _old_shared_suffix_start: usize,
     ) {
     ) {
         todo!()
         todo!()
         // // Should have already diffed the shared-key prefixes and suffixes.
         // // Should have already diffed the shared-key prefixes and suffixes.
@@ -1150,9 +1140,9 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
     // When this function exits, the change list stack remains the same.
     // When this function exits, the change list stack remains the same.
     fn diff_keyed_suffix(
     fn diff_keyed_suffix(
         &self,
         &self,
-        old: &[VNode<'bump>],
-        new: &[VNode<'bump>],
-        new_shared_suffix_start: usize,
+        _old: &[VNode<'bump>],
+        _new: &[VNode<'bump>],
+        _new_shared_suffix_start: usize,
     ) {
     ) {
         todo!()
         todo!()
         //     debug_assert_eq!(old.len(), new.len());
         //     debug_assert_eq!(old.len(), new.len());
@@ -1190,7 +1180,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
         //     [... parent child]
         //     [... parent child]
 
 
         // todo!()
         // todo!()
-        for (i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
+        for (_i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
             // [... parent prev_child]
             // [... parent prev_child]
             // self.dom.go_to_sibling(i);
             // self.dom.go_to_sibling(i);
             // [... parent this_child]
             // [... parent this_child]
@@ -1259,7 +1249,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
     pub fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) {
     pub fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) {
         // debug_assert!(self.dom.traversal_is_committed());
         // debug_assert!(self.dom.traversal_is_committed());
         for child in old {
         for child in old {
-            if let VNode::Component(vcomp) = child {
+            if let VNodeKind::Component(_vcomp) = child.kind {
                 // dom
                 // dom
                 //     .create_text_node("placeholder for vcomponent");
                 //     .create_text_node("placeholder for vcomponent");
 
 
@@ -1328,23 +1318,23 @@ impl<'a> Iterator for RealChildIterator<'a> {
 
 
         while returned_node.is_none() {
         while returned_node.is_none() {
             if let Some((count, node)) = self.stack.last_mut() {
             if let Some((count, node)) = self.stack.last_mut() {
-                match node {
+                match &node.kind {
                     // We can only exit our looping when we get "real" nodes
                     // We can only exit our looping when we get "real" nodes
                     // This includes fragments and components when they're empty (have a single root)
                     // This includes fragments and components when they're empty (have a single root)
-                    VNode::Element(_) | VNode::Text(_) => {
+                    VNodeKind::Element(_) | VNodeKind::Text(_) => {
                         // We've recursed INTO an element/text
                         // We've recursed INTO an element/text
                         // We need to recurse *out* of it and move forward to the next
                         // We need to recurse *out* of it and move forward to the next
                         should_pop = true;
                         should_pop = true;
-                        returned_node = node.get_mounted_id(&self.scopes);
+                        returned_node = Some(node.dom_id.get());
                     }
                     }
 
 
                     // If we get a fragment we push the next child
                     // If we get a fragment we push the next child
-                    VNode::Fragment(frag) => {
+                    VNodeKind::Fragment(frag) => {
                         let subcount = *count as usize;
                         let subcount = *count as usize;
 
 
                         if frag.children.len() == 0 {
                         if frag.children.len() == 0 {
                             should_pop = true;
                             should_pop = true;
-                            returned_node = node.get_mounted_id(&self.scopes);
+                            returned_node = Some(node.dom_id.get());
                         }
                         }
 
 
                         if subcount >= frag.children.len() {
                         if subcount >= frag.children.len() {
@@ -1355,11 +1345,11 @@ impl<'a> Iterator for RealChildIterator<'a> {
                     }
                     }
 
 
                     // Immediately abort suspended nodes - can't do anything with them yet
                     // Immediately abort suspended nodes - can't do anything with them yet
-                    // VNode::Suspended => should_pop = true,
-                    VNode::Suspended { real } => todo!(),
+                    // VNodeKind::Suspended => should_pop = true,
+                    VNodeKind::Suspended => todo!(),
 
 
                     // For components, we load their root and push them onto the stack
                     // For components, we load their root and push them onto the stack
-                    VNode::Component(sc) => {
+                    VNodeKind::Component(sc) => {
                         let scope = self.scopes.try_get(sc.ass_scope.get().unwrap()).unwrap();
                         let scope = self.scopes.try_get(sc.ass_scope.get().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
@@ -1388,3 +1378,13 @@ impl<'a> Iterator for RealChildIterator<'a> {
         returned_node
         returned_node
     }
     }
 }
 }
+
+fn compare_strs(a: &str, b: &str) -> bool {
+    // Check by pointer, optimizing for static strs
+    if !std::ptr::eq(a, b) {
+        // If the pointers are different then check by value
+        a == b
+    } else {
+        true
+    }
+}

+ 1 - 2
packages/core/src/events.rs

@@ -4,7 +4,7 @@
 //! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
 //! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
 //! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
 //! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
 
 
-use std::{cell::Cell, ops::Deref, rc::Rc};
+
 
 
 use crate::innerlude::{RealDomNode, ScopeIdx};
 use crate::innerlude::{RealDomNode, ScopeIdx};
 
 
@@ -125,7 +125,6 @@ pub mod on {
     use std::{fmt::Debug, ops::Deref, rc::Rc};
     use std::{fmt::Debug, ops::Deref, rc::Rc};
 
 
     use crate::{
     use crate::{
-        innerlude::ElementBuilder,
         innerlude::NodeFactory,
         innerlude::NodeFactory,
         innerlude::{Attribute, Listener, RealDomNode, VNode},
         innerlude::{Attribute, Listener, RealDomNode, VNode},
     };
     };

+ 32 - 39
packages/core/src/lib.rs

@@ -1,3 +1,4 @@
+#![allow(non_snake_case, dead_code, unused_must_use, unreachable_code)]
 //! Dioxus Core
 //! Dioxus Core
 //! ----------
 //! ----------
 //!
 //!
@@ -8,25 +9,20 @@
 //!
 //!
 //!
 //!
 
 
-#[cfg(feature = "serialize")]
-pub mod serialize;
+pub use crate::innerlude::{
+    format_args_f, html, rsx, DioxusElement, DomEdit, EventTrigger, LazyNodes, NodeFactory,
+    Properties, RealDom, RealDomNode, ScopeIdx, VNode, VNodeKind, VirtualDom, VirtualEvent, FC,
+};
 
 
-pub mod arena;
-pub mod bumpframe;
-pub mod component;
-pub mod context;
-pub mod diff;
-pub mod error;
-pub mod events;
-pub mod hooklist;
-pub mod nodebuilder;
-pub mod nodes;
-pub mod scope;
-pub mod signals;
-pub mod styles;
-pub mod tasks;
-pub mod util;
-pub mod virtual_dom;
+pub mod prelude {
+    pub use crate::component::{fc_to_builder, Fragment, Properties};
+    pub use crate::context::Context;
+    pub use crate::innerlude::DioxusElement;
+    pub use crate::innerlude::{LazyNodes, NodeFactory, FC};
+    pub use crate::nodes::VNode;
+    pub use crate::VirtualDom;
+    pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
+}
 
 
 // types used internally that are important
 // types used internally that are important
 pub(crate) mod innerlude {
 pub(crate) mod innerlude {
@@ -36,7 +32,6 @@ pub(crate) mod innerlude {
     pub use crate::diff::*;
     pub use crate::diff::*;
     pub use crate::error::*;
     pub use crate::error::*;
     pub use crate::events::*;
     pub use crate::events::*;
-    pub use crate::nodebuilder::*;
     pub use crate::nodes::*;
     pub use crate::nodes::*;
     pub use crate::scope::*;
     pub use crate::scope::*;
     pub use crate::serialize::*;
     pub use crate::serialize::*;
@@ -46,28 +41,26 @@ pub(crate) mod innerlude {
 
 
     pub type FC<P> = fn(Context<P>) -> VNode;
     pub type FC<P> = fn(Context<P>) -> VNode;
 
 
-    // Re-export the FC macro
-    pub use crate::nodebuilder as builder;
-    pub use dioxus_core_macro::{html, rsx};
-}
-
-pub use crate::{
-    innerlude::{
-        DioxusElement, DomEdit, LazyNodes, NodeFactory, RealDom, RealDomNode, ScopeIdx, FC,
-    },
-    virtual_dom::VirtualDom,
-};
-
-pub mod prelude {
-    pub use crate::component::{fc_to_builder, Fragment, Properties};
-    pub use crate::context::Context;
-    pub use crate::innerlude::{LazyNodes, NodeFactory, FC};
-    pub use crate::nodebuilder::DioxusElement;
-    pub use crate::nodes::VNode;
-    pub use crate::VirtualDom;
-    pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
+    pub use dioxus_core_macro::{format_args_f, html, rsx};
 }
 }
 
 
 pub mod exports {
 pub mod exports {
     // export important things here
     // export important things here
 }
 }
+
+pub mod arena;
+pub mod bumpframe;
+pub mod component;
+pub mod context;
+pub mod diff;
+pub mod error;
+pub mod events;
+pub mod hooklist;
+pub mod nodes;
+pub mod scope;
+#[cfg(feature = "serialize")]
+pub mod serialize;
+pub mod signals;
+pub mod tasks;
+pub mod util;
+pub mod virtual_dom;

+ 337 - 378
packages/core/src/nodes.rs

@@ -4,225 +4,70 @@
 //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
 //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
 
 
 use crate::{
 use crate::{
-    arena::SharedArena,
     events::VirtualEvent,
     events::VirtualEvent,
-    innerlude::{Context, Properties, RealDom, RealDomNode, Scope, ScopeIdx, FC},
-    nodebuilder::NodeFactory,
+    innerlude::{Context, Properties, RealDomNode, Scope, ScopeIdx, FC},
 };
 };
-use appendlist::AppendList;
-use bumpalo::Bump;
 use std::{
 use std::{
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
     fmt::{Arguments, Debug, Formatter},
     fmt::{Arguments, Debug, Formatter},
+    marker::PhantomData,
     rc::Rc,
     rc::Rc,
 };
 };
 
 
+pub struct VNode<'src> {
+    pub kind: VNodeKind<'src>,
+    pub dom_id: Cell<RealDomNode>,
+    pub key: Option<&'src str>,
+}
+
 /// Tools for the base unit of the virtual dom - the VNode
 /// Tools for the base unit of the virtual dom - the VNode
 /// VNodes are intended to be quickly-allocated, lightweight enum values.
 /// VNodes are intended to be quickly-allocated, lightweight enum values.
 ///
 ///
 /// Components will be generating a lot of these very quickly, so we want to
 /// Components will be generating a lot of these very quickly, so we want to
 /// limit the amount of heap allocations / overly large enum sizes.
 /// limit the amount of heap allocations / overly large enum sizes.
-pub enum VNode<'src> {
-    /// An element node (node type `ELEMENT_NODE`).
-    Element(&'src VElement<'src>),
-
-    /// A text node (node type `TEXT_NODE`).
+pub enum VNodeKind<'src> {
     Text(VText<'src>),
     Text(VText<'src>),
-
-    /// A fragment is a list of elements that might have a dynamic order.
-    /// Normally, children will have a fixed order. However, Fragments allow a dynamic order and must be diffed differently.
-    ///
-    /// Fragments don't have a single mount into the dom, so their position is characterized by the head and tail nodes.
-    ///
-    /// Fragments may have children and keys
-    Fragment(&'src VFragment<'src>),
-
-    /// A "suspended component"
-    /// This is a masqeurade over an underlying future that needs to complete
-    /// When the future is completed, the VNode will then trigger a render and the `real` field gets populated
-    Suspended { real: Cell<RealDomNode> },
-
-    /// A User-defined componen node (node type COMPONENT_NODE)
+    Element(&'src VElement<'src>),
+    Fragment(VFragment<'src>),
     Component(&'src VComponent<'src>),
     Component(&'src VComponent<'src>),
+    Suspended,
 }
 }
 
 
-// it's okay to clone because vnodes are just references to places into the bump
-impl<'a> Clone for VNode<'a> {
-    fn clone(&self) -> Self {
-        match self {
-            VNode::Element(element) => VNode::Element(element),
-            VNode::Text(old) => VNode::Text(old.clone()),
-            VNode::Fragment(fragment) => VNode::Fragment(fragment),
-            VNode::Component(component) => VNode::Component(component),
-            VNode::Suspended { real } => VNode::Suspended { real: real.clone() },
-        }
-    }
+pub struct VText<'src> {
+    pub text: &'src str,
+    pub is_static: bool,
 }
 }
 
 
-impl<'old, 'new> VNode<'old> {
-    // performs a somewhat costly clone of this vnode into another bump
-    // this is used when you want to drag nodes from an old frame into a new frame
-    // There is no way to safely drag listeners over (no way to clone a closure)
-    //
-    // This method will only be called if a component was once a real node and then becomes suspended
-    fn deep_clone_to_new_bump(&self, new: &'new Bump) -> VNode<'new> {
-        match self {
-            VNode::Element(el) => {
-                let new_el: VElement<'new> = VElement {
-                    key: NodeKey::NONE,
-                    // key: el.key.clone(),
-                    tag_name: el.tag_name,
-                    // wipe listeners on deep clone, there's no way to know what other bump material they might be referencing (nodes, etc)
-                    listeners: &[],
-                    attributes: {
-                        let attr_vec = bumpalo::collections::Vec::new_in(new);
-                        attr_vec.into_bump_slice()
-                    },
-                    children: {
-                        let attr_vec = bumpalo::collections::Vec::new_in(new);
-                        attr_vec.into_bump_slice()
-                    },
-                    namespace: el.namespace.clone(),
-                    dom_id: el.dom_id.clone(),
-                    is_static: el.is_static.clone(),
-                };
-
-                VNode::Element(new.alloc_with(move || new_el))
-            }
-            VNode::Text(_) => todo!(),
-            VNode::Fragment(_) => todo!(),
-            VNode::Suspended { real } => todo!(),
-            VNode::Component(_) => todo!(),
-        }
-    }
+pub struct VFragment<'src> {
+    pub children: &'src [VNode<'src>],
+    pub is_static: bool,
 }
 }
 
 
-impl<'a> VNode<'a> {
-    /// Low-level constructor for making a new `Node` of type element with given
-    /// parts.
-    ///
-    /// This is primarily intended for JSX and templating proc-macros to compile
-    /// down into. If you are building nodes by-hand, prefer using the
-    /// `dodrio::builder::*` APIs.
+pub trait DioxusElement {
+    const TAG_NAME: &'static str;
+    const NAME_SPACE: Option<&'static str>;
     #[inline]
     #[inline]
-    pub fn element(
-        bump: &'a Bump,
-        key: NodeKey<'a>,
-        tag_name: &'static str,
-        listeners: &'a [Listener<'a>],
-        attributes: &'a [Attribute<'a>],
-        children: &'a [VNode<'a>],
-        namespace: Option<&'static str>,
-    ) -> VNode<'a> {
-        let element = bump.alloc_with(|| VElement {
-            key,
-            tag_name,
-            listeners,
-            attributes,
-            children,
-            namespace,
-            dom_id: Cell::new(RealDomNode::empty()),
-            is_static: Cell::new(false),
-        });
-        VNode::Element(element)
-    }
-
-    pub fn static_text(text: &'static str) -> VNode {
-        VNode::Text(VText {
-            text,
-            is_static: true,
-            dom_id: Cell::new(RealDomNode::empty()),
-        })
+    fn tag_name(&self) -> &'static str {
+        Self::TAG_NAME
     }
     }
-    /// Construct a new text node with the given text.
-    pub fn text(bump: &'a Bump, args: Arguments) -> VNode<'a> {
-        match args.as_str() {
-            Some(text) => VNode::static_text(text),
-            None => {
-                use bumpalo::core_alloc::fmt::Write;
-                let mut s = bumpalo::collections::String::new_in(bump);
-                s.write_fmt(args).unwrap();
-                VNode::Text(VText {
-                    text: s.into_bump_str(),
-                    is_static: false,
-                    dom_id: Cell::new(RealDomNode::empty()),
-                })
-            }
-        }
-    }
-
     #[inline]
     #[inline]
-    pub(crate) fn key(&self) -> NodeKey {
-        match &self {
-            VNode::Text { .. } => NodeKey::NONE,
-            VNode::Element(e) => e.key,
-            VNode::Fragment(frag) => frag.key,
-            VNode::Component(c) => c.key,
-
-            // todo suspend should be allowed to have keys
-            VNode::Suspended { .. } => NodeKey::NONE,
-        }
-    }
-
-    fn get_child(&self, id: u32) -> Option<&'a VNode<'a>> {
-        todo!()
-    }
-
-    pub fn is_real(&self) -> bool {
-        match self {
-            VNode::Element(_) => true,
-            VNode::Text(_) => true,
-            VNode::Fragment(_) => false,
-            VNode::Suspended { .. } => false,
-            VNode::Component(_) => false,
-        }
-    }
-
-    pub fn get_mounted_id(&self, components: &SharedArena) -> Option<RealDomNode> {
-        match self {
-            VNode::Element(el) => Some(el.dom_id.get()),
-            VNode::Text(te) => Some(te.dom_id.get()),
-            VNode::Fragment(frag) => frag.void_root.get(),
-            VNode::Suspended { .. } => todo!(),
-            VNode::Component(el) => todo!(),
-        }
-    }
-}
-
-impl Debug for VNode<'_> {
-    fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
-        match self {
-            VNode::Element(el) => write!(s, "element, {}", el.tag_name),
-            VNode::Text(t) => write!(s, "text, {}", t.text),
-            VNode::Fragment(_) => write!(s, "fragment"),
-            VNode::Suspended { .. } => write!(s, "suspended"),
-            VNode::Component(_) => write!(s, "component"),
-        }
+    fn namespace(&self) -> Option<&'static str> {
+        Self::NAME_SPACE
     }
     }
 }
 }
-
-#[derive(Clone)]
-pub struct VText<'src> {
-    pub text: &'src str,
-    pub is_static: bool,
-    pub dom_id: Cell<RealDomNode>,
-}
-
-// ========================================================
-//   VElement (div, h1, etc), attrs, keys, listener handle
-// ========================================================
-
-#[derive(Clone)]
 pub struct VElement<'a> {
 pub struct VElement<'a> {
-    /// Elements have a tag name, zero or more attributes, and zero or more
-    pub key: NodeKey<'a>,
+    // tag is always static
     pub tag_name: &'static str,
     pub tag_name: &'static str,
+    pub namespace: Option<&'static str>,
+
+    pub static_listeners: bool,
     pub listeners: &'a [Listener<'a>],
     pub listeners: &'a [Listener<'a>],
+
+    pub static_attrs: bool,
     pub attributes: &'a [Attribute<'a>],
     pub attributes: &'a [Attribute<'a>],
+
+    pub static_children: bool,
     pub children: &'a [VNode<'a>],
     pub children: &'a [VNode<'a>],
-    pub namespace: Option<&'static str>,
-    pub dom_id: Cell<RealDomNode>,
-    pub is_static: Cell<bool>,
 }
 }
 
 
 /// An attribute on a DOM node, such as `id="my-thing"` or
 /// An attribute on a DOM node, such as `id="my-thing"` or
@@ -232,175 +77,185 @@ pub struct Attribute<'a> {
     pub name: &'static str,
     pub name: &'static str,
     pub value: &'a str,
     pub value: &'a str,
     pub is_static: bool,
     pub is_static: bool,
-
-    /// If an attribute is "namespaced", then it belongs to a group
-    /// The most common namespace is the "style" namespace
-    // pub is_dynamic: bool,
+    pub is_volatile: bool,
+    // Doesn't exist in the html spec, mostly used to denote "style" tags - could be for any type of group
     pub namespace: Option<&'static str>,
     pub namespace: Option<&'static str>,
 }
 }
 
 
-impl<'a> Attribute<'a> {
-    /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
-    #[inline]
-    pub fn name(&self) -> &'a str {
-        self.name
-    }
-
-    /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
-    #[inline]
-    pub fn value(&self) -> &'a str {
-        self.value
-    }
-
-    /// Certain attributes are considered "volatile" and can change via user
-    /// input that we can't see when diffing against the old virtual DOM. For
-    /// these attributes, we want to always re-set the attribute on the physical
-    /// DOM node, even if the old and new virtual DOM nodes have the same value.
-    #[inline]
-    pub(crate) fn is_volatile(&self) -> bool {
-        match self.name {
-            "value" | "checked" | "selected" => true,
-            _ => false,
-        }
-    }
-}
-
-pub struct ListenerHandle {
-    pub event: &'static str,
-    pub scope: ScopeIdx,
-    pub id: usize,
-}
-
 /// An event listener.
 /// An event listener.
+/// IE onclick, onkeydown, etc
 pub struct Listener<'bump> {
 pub struct Listener<'bump> {
     /// The type of event to listen for.
     /// The type of event to listen for.
     pub(crate) event: &'static str,
     pub(crate) event: &'static str,
-
-    /// Which scope?
-    /// This might not actually be relevant
     pub scope: ScopeIdx,
     pub scope: ScopeIdx,
-
     pub mounted_node: &'bump Cell<RealDomNode>,
     pub mounted_node: &'bump Cell<RealDomNode>,
-
-    /// The callback to invoke when the event happens.
     pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
     pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
 }
 }
 
 
-/// The key for keyed children.
-///
-/// Keys must be unique among siblings.
-///
-/// If any sibling is keyed, then they all must be keyed.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct NodeKey<'a>(pub(crate) Option<&'a str>);
+/// Virtual Components for custom user-defined components
+/// Only supports the functional syntax
+pub struct VComponent<'src> {
+    pub ass_scope: Cell<Option<ScopeIdx>>,
+    pub(crate) caller: Rc<dyn Fn(&Scope) -> VNode>,
+    pub(crate) children: &'src [VNode<'src>],
+    pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
+    pub is_static: bool,
 
 
-impl<'a> Default for NodeKey<'a> {
-    fn default() -> NodeKey<'a> {
-        NodeKey::NONE
-    }
+    // a pointer into the bump arena (given by the 'src lifetime)
+    pub(crate) raw_props: *const (),
+
+    // a pointer to the raw fn typ
+    pub(crate) user_fc: *const (),
 }
 }
-impl<'a> NodeKey<'a> {
-    /// The default, lack of a key.
-    pub const NONE: NodeKey<'a> = NodeKey(None);
 
 
-    /// Is this key `NodeKey::NONE`?
-    #[inline]
-    pub fn is_none(&self) -> bool {
-        *self == Self::NONE
-    }
+/// This struct provides an ergonomic API to quickly build VNodes.
+///
+/// NodeFactory is used to build VNodes in the component's memory space.
+/// This struct adds metadata to the final VNode about listeners, attributes, and children
+#[derive(Copy, Clone)]
+pub struct NodeFactory<'a> {
+    pub scope_ref: &'a Scope,
+    pub listener_id: &'a Cell<usize>,
+}
 
 
-    /// Is this key not `NodeKey::NONE`?
+impl<'a> NodeFactory<'a> {
     #[inline]
     #[inline]
-    pub fn is_some(&self) -> bool {
-        !self.is_none()
+    pub fn bump(&self) -> &'a bumpalo::Bump {
+        &self.scope_ref.cur_frame().bump
     }
     }
 
 
-    /// Create a new `NodeKey`.
-    ///
-    /// `key` must not be `u32::MAX`.
-    #[inline]
-    pub fn new(key: &'a str) -> Self {
-        NodeKey(Some(key))
+    pub const fn const_text(&self, text: &'static str) -> VNodeKind<'static> {
+        VNodeKind::Text(VText {
+            is_static: true,
+            text,
+        })
     }
     }
 
 
-    #[inline]
-    pub fn new_opt(key: Option<&'a str>) -> Self {
-        NodeKey(key)
+    pub const fn const_fragment(&self, children: &'static [VNode<'static>]) -> VNodeKind<'static> {
+        VNodeKind::Fragment(VFragment {
+            children,
+            is_static: true,
+        })
     }
     }
-}
-
-// ==============================
-//   Custom components
-// ==============================
-
-/// Virtual Components for custom user-defined components
-/// Only supports the functional syntax
-pub type StableScopeAddres = Option<u32>;
-pub type VCompAssociatedScope = Option<ScopeIdx>;
 
 
-pub struct VComponent<'src> {
-    pub key: NodeKey<'src>,
+    pub fn static_text(text: &'static str) -> VNode {
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Text(VText {
+                text,
+                is_static: true,
+            }),
+        }
+    }
 
 
-    // pub void_root: Cell<Option<RealDomNode>>,
-    pub ass_scope: Cell<VCompAssociatedScope>,
+    pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
+        match args.as_str() {
+            Some(static_str) => (static_str, true),
+            None => {
+                use bumpalo::core_alloc::fmt::Write;
+                let mut s = bumpalo::collections::String::new_in(self.bump());
+                s.write_fmt(args).unwrap();
+                (s.into_bump_str(), false)
+            }
+        }
+    }
 
 
-    // todo: swap the RC out with
-    pub caller: Rc<dyn Fn(&Scope) -> VNode>,
+    /// Create some text that's allocated along with the other vnodes
+    pub fn text(&self, args: Arguments) -> VNode<'a> {
+        let (text, is_static) = self.raw_text(args);
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Text(VText { text, is_static }),
+        }
+    }
 
 
-    pub children: &'src [VNode<'src>],
+    pub fn raw_element(
+        &self,
+        _tag: &'static str,
+        _listeners: &[Listener],
+        _attributes: &[Attribute],
+        _children: &'a [VNode<'a>],
+    ) -> VNode<'a> {
+        todo!()
+    }
 
 
-    pub comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
+    pub fn element(
+        &self,
+        el: impl DioxusElement,
+        listeners: &'a [Listener<'a>],
+        attributes: &'a [Attribute<'a>],
+        children: &'a [VNode<'a>],
+        key: Option<&'a str>,
+    ) -> VNode<'a> {
+        let mut queue = self.scope_ref.listeners.borrow_mut();
+        for listener in listeners {
+            let mounted = listener.mounted_node as *const _ as *mut _;
+            let callback = listener.callback as *const _ as *mut _;
+            queue.push((mounted, callback))
+        }
 
 
-    pub is_static: bool,
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key,
+            kind: VNodeKind::Element(self.bump().alloc(VElement {
+                tag_name: el.tag_name(),
+                namespace: el.namespace(),
+                static_listeners: false,
+                listeners,
+                static_attrs: false,
+                attributes,
+                static_children: false,
+                children,
+            })),
+        }
+    }
 
 
-    // a pointer into the bump arena (given by the 'src lifetime)
-    // raw_props: Box<dyn Any>,
-    raw_props: *const (),
+    pub fn suspended() -> VNode<'static> {
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Suspended,
+        }
+    }
 
 
-    // a pointer to the raw fn typ
-    pub user_fc: *const (),
-}
+    pub fn attr(
+        &self,
+        name: &'static str,
+        val: Arguments,
+        namespace: Option<&'static str>,
+        is_volatile: bool,
+    ) -> Attribute<'a> {
+        let (value, is_static) = self.raw_text(val);
+        Attribute {
+            name,
+            value,
+            is_static,
+            namespace,
+            is_volatile,
+        }
+    }
 
 
-impl<'a> VComponent<'a> {
-    /// When the rsx! macro is called, it will check if the CanMemo flag is set to true (from the Props impl)
-    /// If it is set to true, then this method will be called which implements automatic memoization.
-    ///
-    /// If the CanMemo is `false`, then the macro will call the backup method which always defaults to "false"
-    pub fn new<P: Properties + 'a>(
-        cx: &NodeFactory<'a>,
+    pub fn virtual_child<P>(
+        &self,
         component: FC<P>,
         component: FC<P>,
         props: P,
         props: P,
-        key: Option<&'a str>,
+        key: Option<&'a str>, // key: NodeKey<'a>,
         children: &'a [VNode<'a>],
         children: &'a [VNode<'a>],
-    ) -> Self {
-        let bump = cx.bump();
-        let user_fc = component as *const ();
-
-        let props = bump.alloc(props);
+    ) -> VNode<'a>
+    where
+        P: Properties + 'a,
+    {
+        // We don't want the fat part of the fat pointer
+        // This function does static dispatch so we don't need any VTable stuff
+        let props = self.bump().alloc(props);
         let raw_props = props as *const P as *const ();
         let raw_props = props as *const P as *const ();
 
 
-        let comparator: Option<&dyn Fn(&VComponent) -> bool> = Some(bump.alloc_with(|| {
+        let user_fc = component as *const ();
+
+        let comparator: Option<&dyn Fn(&VComponent) -> bool> = Some(self.bump().alloc_with(|| {
             move |other: &VComponent| {
             move |other: &VComponent| {
-                // Safety:
-                // ------
-                //
-                // Invariants:
-                // - Component function pointers are the same
-                // - Generic properties on the same function pointer are the same
-                // - Lifetime of P borrows from its parent
-                // - The parent scope still exists when method is called
-                // - Casting from T to *const () is portable
-                // - Casting raw props to P can only happen when P is static
-                //
-                // Explanation:
-                //   We are guaranteed that the props will be of the same type because
-                //   there is no way to create a VComponent other than this `new` method.
-                //
-                //   Therefore, if the render functions are identical (by address), then so will be
-                //   props type paramter (because it is the same render function). Therefore, we can be
-                //   sure that it is safe to interperet the previous props raw pointer as the same props
-                //   type. From there, we can call the props' "memoize" method to see if we can
-                //   avoid re-rendering the component.
                 if user_fc == other.user_fc {
                 if user_fc == other.user_fc {
                     let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
                     let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
                     let props_memoized = unsafe { props.memoize(&real_other) };
                     let props_memoized = unsafe { props.memoize(&real_other) };
@@ -414,81 +269,185 @@ impl<'a> VComponent<'a> {
             }
             }
         }));
         }));
 
 
-        let key = match key {
-            Some(key) => NodeKey::new(key),
-            None => NodeKey(None),
-        };
-
-        let caller = create_component_caller(component, raw_props);
+        VNode {
+            key,
+            dom_id: Cell::new(RealDomNode::empty()),
+            kind: VNodeKind::Component(self.bump().alloc_with(|| VComponent {
+                user_fc,
+                comparator,
+                raw_props,
+                children,
+                caller: NodeFactory::create_component_caller(component, raw_props),
+                is_static: children.len() == 0 && P::IS_STATIC && key.is_none(),
+                ass_scope: Cell::new(None),
+            })),
+        }
+    }
 
 
-        // If the component does not have children, has no props (we can't memoize props), and has no no key, then we don't
-        // need to bother diffing it in the future
-        //
-        // This is more of an optimization to prevent unnecessary descending through the tree during diffing, rather than
-        // actually speeding up the diff process itself
-        let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
+    pub fn create_component_caller<'g, P: 'g>(
+        component: FC<P>,
+        raw_props: *const (),
+    ) -> Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r>> {
+        type Captured<'a> = Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r> + 'a>;
+        let caller: Captured = Rc::new(move |scp: &Scope| -> VNode {
+            // cast back into the right lifetime
+            let safe_props: &'_ P = unsafe { &*(raw_props as *const P) };
+            let cx: Context<P> = Context {
+                props: safe_props,
+                scope: scp,
+            };
+
+            let res = component(cx);
+
+            let g2 = unsafe { std::mem::transmute(res) };
+
+            g2
+        });
+        unsafe { std::mem::transmute::<_, Captured<'static>>(caller) }
+    }
 
 
-        Self {
-            user_fc,
-            comparator,
-            raw_props,
-            children,
-            ass_scope: Cell::new(None),
-            key,
-            caller,
-            is_static,
-            // void_root: Cell::new(None),
+    pub fn fragment_from_iter(
+        self,
+        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
+    ) -> VNode<'a> {
+        let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
+        // TODO throw an error if there are nodes without keys
+        for node in node_iter.into_iter() {
+            nodes.push(node.into_vnode(self));
+        }
+        VNode {
+            dom_id: RealDomNode::empty_cell(),
+            key: None,
+            kind: VNodeKind::Fragment(VFragment {
+                children: nodes.into_bump_slice(),
+                is_static: false,
+            }),
         }
         }
     }
     }
 }
 }
 
 
-type Captured<'a> = Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r> + 'a>;
-
-pub fn create_component_caller<'a, P: 'a>(
-    user_component: FC<P>,
-    raw_props: *const (),
-) -> Rc<dyn for<'r> Fn(&'r Scope) -> VNode<'r>> {
-    let g: Captured = Rc::new(move |scp: &Scope| -> VNode {
-        // cast back into the right lifetime
-        let safe_props: &'_ P = unsafe { &*(raw_props as *const P) };
-        let tasks = RefCell::new(Vec::new());
-        let cx: Context<P> = Context {
-            props: safe_props,
-            scope: scp,
-            tasks: &tasks,
-        };
+impl<'a> IntoIterator for VNode<'a> {
+    type Item = VNode<'a>;
+    type IntoIter = std::iter::Once<Self::Item>;
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::once(self)
+    }
+}
+impl<'a> IntoVNode<'a> for VNode<'a> {
+    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
+        self
+    }
+}
+
+impl<'a> IntoVNode<'a> for &VNode<'a> {
+    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
+        self.clone()
+    }
+}
+
+pub trait IntoVNode<'a> {
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
+}
 
 
-        let g = user_component(cx);
+// Wrap the the node-builder closure in a concrete type.
+// ---
+// This is a bit of a hack to implement the IntoVNode trait for closure types.
+pub struct LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    inner: G,
+    _p: PhantomData<&'a ()>,
+}
 
 
-        for task in tasks.borrow_mut().drain(..) {
-            scp.submit_task(task);
+impl<'a, G> LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    pub fn new(f: G) -> Self {
+        Self {
+            inner: f,
+            _p: PhantomData {},
         }
         }
+    }
+}
+
+// Cover the cases where nodes are used by macro.
+// Likely used directly.
+// ---
+//  let nodes = rsx!{ ... };
+//  rsx! { {nodes } }
+impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        (self.inner)(cx)
+    }
+}
 
 
-        let g2 = unsafe { std::mem::transmute(g) };
+// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
+impl<'a, G> IntoIterator for LazyNodes<'a, G>
+where
+    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    type Item = Self;
+    type IntoIter = std::iter::Once<Self::Item>;
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::once(self)
+    }
+}
 
 
-        g2
-    });
-    let r: Captured<'static> = unsafe { std::mem::transmute(g) };
-    r
+impl IntoVNode<'_> for () {
+    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        cx.fragment_from_iter(None as Option<VNode>)
+    }
 }
 }
 
 
-pub struct VFragment<'src> {
-    pub key: NodeKey<'src>,
-    pub children: &'src [VNode<'src>],
-    pub void_root: Cell<Option<RealDomNode>>,
+impl IntoVNode<'_> for Option<()> {
+    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        cx.fragment_from_iter(None as Option<VNode>)
+    }
 }
 }
 
 
-impl<'a> VFragment<'a> {
-    pub fn new(key: Option<&'a str>, children: &'a [VNode<'a>]) -> Self {
-        let key = match key {
-            Some(key) => NodeKey::new(key),
-            None => NodeKey(None),
+impl Debug for NodeFactory<'_> {
+    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        Ok(())
+    }
+}
+
+// it's okay to clone because vnodes are just references to places into the bump
+impl<'a> Clone for VNode<'a> {
+    fn clone(&self) -> Self {
+        let kind = match &self.kind {
+            VNodeKind::Element(element) => VNodeKind::Element(element),
+            VNodeKind::Text(old) => VNodeKind::Text(VText {
+                text: old.text,
+                is_static: old.is_static,
+            }),
+            VNodeKind::Fragment(fragment) => VNodeKind::Fragment(VFragment {
+                children: fragment.children,
+                is_static: fragment.is_static,
+            }),
+            VNodeKind::Component(component) => VNodeKind::Component(component),
+            VNodeKind::Suspended => VNodeKind::Suspended,
         };
         };
+        VNode {
+            kind,
+            dom_id: self.dom_id.clone(),
+            key: self.key.clone(),
+        }
+    }
+}
 
 
-        Self {
-            key,
-            children,
-            void_root: Cell::new(None),
+impl Debug for VNode<'_> {
+    fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+        match &self.kind {
+            VNodeKind::Element(el) => write!(s, "element, {}", el.tag_name),
+            VNodeKind::Text(t) => write!(s, "text, {}", t.text),
+            VNodeKind::Fragment(_) => write!(s, "fragment"),
+            VNodeKind::Suspended { .. } => write!(s, "suspended"),
+            VNodeKind::Component(_) => write!(s, "component"),
         }
         }
     }
     }
 }
 }

+ 0 - 265
packages/core/src/nodebuilder.rs → packages/core/src/oldbuilder.rs

@@ -1,25 +1,3 @@
-//! Helpers for building virtual DOM VNodes.
-
-use std::{
-    any::Any,
-    borrow::BorrowMut,
-    cell::{Cell, RefCell},
-    fmt::Arguments,
-    intrinsics::transmute,
-    marker::PhantomData,
-    u128,
-};
-
-use bumpalo::Bump;
-
-use crate::{
-    events::VirtualEvent,
-    innerlude::{Properties, RealDomNode, Scope, VComponent, VFragment, VText, FC},
-    nodes::{Attribute, Listener, NodeKey, VNode},
-};
-
-/// A virtual DOM element builder.
-///
 /// Typically constructed with element-specific constructors, eg the `div`
 /// Typically constructed with element-specific constructors, eg the `div`
 /// function for building `<div>` elements or the `button` function for building
 /// function for building `<div>` elements or the `button` function for building
 /// `<button>` elements.
 /// `<button>` elements.
@@ -504,246 +482,3 @@ To help you identify where this error is coming from, we've generated a backtrac
         self
         self
     }
     }
 }
 }
-
-impl<'a> IntoIterator for VNode<'a> {
-    type Item = VNode<'a>;
-    type IntoIter = std::iter::Once<Self::Item>;
-    fn into_iter(self) -> Self::IntoIter {
-        std::iter::once(self)
-    }
-}
-impl<'a> IntoVNode<'a> for VNode<'a> {
-    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
-        self
-    }
-}
-
-impl<'a> IntoVNode<'a> for &VNode<'a> {
-    fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
-        self.clone()
-    }
-}
-
-pub trait IntoVNode<'a> {
-    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
-}
-
-// Wrap the the node-builder closure in a concrete type.
-// ---
-// This is a bit of a hack to implement the IntoVNode trait for closure types.
-pub struct LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    inner: G,
-    _p: PhantomData<&'a ()>,
-}
-
-impl<'a, G> LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    pub fn new(f: G) -> Self {
-        Self {
-            inner: f,
-            _p: PhantomData {},
-        }
-    }
-}
-
-// Cover the cases where nodes are used by macro.
-// Likely used directly.
-// ---
-//  let nodes = rsx!{ ... };
-//  rsx! { {nodes } }
-impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        (self.inner)(cx)
-    }
-}
-
-// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
-impl<'a, G> IntoIterator for LazyNodes<'a, G>
-where
-    G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    type Item = Self;
-    type IntoIter = std::iter::Once<Self::Item>;
-    fn into_iter(self) -> Self::IntoIter {
-        std::iter::once(self)
-    }
-}
-
-impl IntoVNode<'_> for () {
-    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        cx.fragment_from_iter(None as Option<VNode>)
-    }
-}
-
-impl IntoVNode<'_> for Option<()> {
-    fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        cx.fragment_from_iter(None as Option<VNode>)
-    }
-}
-
-pub fn raw_text<'a>(bump: &'a bumpalo::Bump, args: std::fmt::Arguments) -> (&'a str, bool) {
-    match args.as_str() {
-        Some(static_str) => (static_str, true),
-        None => {
-            use bumpalo::core_alloc::fmt::Write;
-            let mut s = bumpalo::collections::String::new_in(bump);
-            s.write_fmt(args).unwrap();
-            (s.into_bump_str(), false)
-        }
-    }
-}
-
-pub fn virtual_child<'a, T: Properties + 'a>(
-    cx: NodeFactory<'a>,
-    f: FC<T>,
-    props: T,
-    key: Option<&'a str>, // key: NodeKey<'a>,
-    children: &'a [VNode<'a>],
-) -> VNode<'a> {
-    // currently concerned about if props have a custom drop implementation
-    // might override it with the props macro
-    // todo!()
-    VNode::Component(
-        cx.bump()
-            .alloc(crate::nodes::VComponent::new(&cx, f, props, key, children)),
-    )
-}
-
-pub fn vfragment<'a>(
-    cx: NodeFactory<'a>,
-    key: Option<&'a str>, // key: NodeKey<'a>,
-    children: &'a [VNode<'a>],
-) -> VNode<'a> {
-    VNode::Fragment(cx.bump().alloc(VFragment::new(key, children)))
-}
-
-/// This struct provides an ergonomic API to quickly build VNodes.
-///
-/// NodeFactory is used to build VNodes in the component's memory space.
-/// This struct adds metadata to the final VNode about listeners, attributes, and children
-#[derive(Copy, Clone)]
-pub struct NodeFactory<'a> {
-    pub scope_ref: &'a Scope,
-    pub listener_id: &'a Cell<usize>,
-}
-
-impl<'a> NodeFactory<'a> {
-    #[inline]
-    pub fn bump(&self) -> &'a bumpalo::Bump {
-        &self.scope_ref.cur_frame().bump
-    }
-
-    /// Create some text that's allocated along with the other vnodes
-    pub fn text(&self, args: Arguments) -> VNode<'a> {
-        VNode::text(self.bump(), args)
-    }
-
-    /// Create an element builder
-    pub fn raw_element<'b>(
-        &'b self,
-        tag: &'static str,
-    ) -> ElementBuilder<
-        'a,
-        'b,
-        bumpalo::collections::Vec<'a, Listener<'a>>,
-        bumpalo::collections::Vec<'a, Attribute<'a>>,
-        bumpalo::collections::Vec<'a, VNode<'a>>,
-    > {
-        ElementBuilder::new(self, tag)
-    }
-
-    /// Create an element builder
-    pub fn element<'b>(
-        &'b self,
-        tag: impl DioxusElement,
-    ) -> ElementBuilder<
-        'a,
-        'b,
-        bumpalo::collections::Vec<'a, Listener<'a>>,
-        bumpalo::collections::Vec<'a, Attribute<'a>>,
-        bumpalo::collections::Vec<'a, VNode<'a>>,
-    > {
-        ElementBuilder::new(self, tag.tag_name())
-    }
-
-    pub fn attr(
-        &self,
-        name: &'static str,
-        val: Arguments,
-        namespace: Option<&'static str>,
-    ) -> Attribute<'a> {
-        let (value, is_static) = raw_text(self.bump(), val);
-        Attribute {
-            name,
-            value,
-            is_static,
-            namespace,
-        }
-    }
-
-    pub fn fragment(&self, children: &'a [VNode<'a>], key: Option<&'a str>) -> VNode<'a> {
-        VNode::Fragment(self.bump().alloc(VFragment {
-            children,
-            key: NodeKey::new_opt(key),
-            void_root: Cell::new(None),
-        }))
-    }
-
-    pub fn virtual_child<T, C>(
-        &self,
-        f: FC<T>,
-        props: T,
-        key: Option<&'a str>, // key: NodeKey<'a>,
-        children: C,
-    ) -> VNode<'a>
-    where
-        C: 'a + AsRef<[VNode<'a>]>,
-        T: Properties + 'a,
-    {
-        let children: &'a C = self.bump().alloc(children);
-        VNode::Component(self.bump().alloc(crate::nodes::VComponent::new(
-            self,
-            f,
-            props,
-            key,
-            children.as_ref(),
-        )))
-    }
-
-    pub fn fragment_from_iter(
-        self,
-        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
-    ) -> VNode<'a> {
-        let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
-        for node in node_iter.into_iter() {
-            nodes.push(node.into_vnode(self));
-        }
-        VNode::Fragment(
-            self.bump()
-                .alloc(VFragment::new(None, nodes.into_bump_slice())),
-        )
-    }
-}
-
-use std::fmt::Debug;
-impl Debug for NodeFactory<'_> {
-    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        Ok(())
-    }
-}
-
-pub trait DioxusElement {
-    const TAG_NAME: &'static str;
-    const NAME_SPACE: Option<&'static str>;
-    fn tag_name(&self) -> &'static str {
-        Self::TAG_NAME
-    }
-}

+ 27 - 12
packages/core/src/scope.rs

@@ -1,18 +1,11 @@
 use crate::hooklist::HookList;
 use crate::hooklist::HookList;
 use crate::{arena::SharedArena, innerlude::*};
 use crate::{arena::SharedArena, innerlude::*};
-use appendlist::AppendList;
-use bumpalo::Bump;
-use slotmap::DefaultKey;
-use slotmap::SlotMap;
-use std::marker::PhantomData;
-use std::sync::Arc;
+
 use std::{
 use std::{
     any::{Any, TypeId},
     any::{Any, TypeId},
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
-    collections::{HashMap, HashSet, VecDeque},
-    fmt::Debug,
+    collections::{HashMap, HashSet},
     future::Future,
     future::Future,
-    ops::Deref,
     pin::Pin,
     pin::Pin,
     rc::Rc,
     rc::Rc,
 };
 };
@@ -78,6 +71,8 @@ pub struct Scope {
     pub(crate) suspended_tasks: Vec<*mut Pin<Box<dyn Future<Output = VNode<'static>>>>>,
     pub(crate) suspended_tasks: Vec<*mut Pin<Box<dyn Future<Output = VNode<'static>>>>>,
 }
 }
 
 
+pub type FiberTask = Pin<Box<dyn Future<Output = EventTrigger>>>;
+
 impl Scope {
 impl Scope {
     // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
     // 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.
     // we are going to break this lifetime by force in order to save it on ourselves.
@@ -155,6 +150,17 @@ impl Scope {
         Ok(())
         Ok(())
     }
     }
 
 
+    /// Progress a suspended node
+    pub fn progress_suspended(&mut self) -> Result<()> {
+        // load the hook
+        // downcast to our special state
+        // run just this hook
+        // create a new vnode
+        // diff this new vnode with the original suspended vnode
+
+        Ok(())
+    }
+
     // this is its own function so we can preciesly control how lifetimes flow
     // this is its own function so we can preciesly control how lifetimes flow
     unsafe fn call_user_component<'a>(&'a self, caller: &WrappedCaller) -> VNode<'static> {
     unsafe fn call_user_component<'a>(&'a self, caller: &WrappedCaller) -> VNode<'static> {
         let new_head: VNode<'a> = caller(self);
         let new_head: VNode<'a> = caller(self);
@@ -171,6 +177,11 @@ impl Scope {
             ..
             ..
         } = trigger;
         } = trigger;
 
 
+        if let &VirtualEvent::FiberEvent = &event {
+            log::info!("arrived a fiber event");
+            return Ok(());
+        }
+
         // todo: implement scanning for outdated events
         // todo: implement scanning for outdated events
 
 
         // Convert the raw ptr into an actual object
         // Convert the raw ptr into an actual object
@@ -182,7 +193,7 @@ impl Scope {
             self.arena_idx
             self.arena_idx
         );
         );
 
 
-        let mut listners = self.listeners.borrow_mut();
+        let listners = self.listeners.borrow_mut();
 
 
         // let listener = listners.get(trigger);
         // let listener = listners.get(trigger);
         let raw_listener = listners.iter().find(|(domptr, _)| {
         let raw_listener = listners.iter().find(|(domptr, _)| {
@@ -207,23 +218,27 @@ impl Scope {
         Ok(())
         Ok(())
     }
     }
 
 
-    pub fn submit_task(&self, task: &mut Pin<Box<dyn Future<Output = ()>>>) {
+    pub fn submit_task(&self, task: FiberTask) {
         log::debug!("Task submitted into scope");
         log::debug!("Task submitted into scope");
-        (self.task_submitter)(DTask::new(task, self.arena_idx));
+        (self.task_submitter)(task);
     }
     }
 
 
+    #[inline]
     pub(crate) fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
     pub(crate) fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
         self.frames.current_head_node()
         self.frames.current_head_node()
     }
     }
 
 
+    #[inline]
     pub(crate) fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
     pub(crate) fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
         self.frames.prev_head_node()
         self.frames.prev_head_node()
     }
     }
 
 
+    #[inline]
     pub(crate) fn cur_frame(&self) -> &BumpFrame {
     pub(crate) fn cur_frame(&self) -> &BumpFrame {
         self.frames.cur_frame()
         self.frames.cur_frame()
     }
     }
 
 
+    #[inline]
     pub fn root<'a>(&'a self) -> &'a VNode<'a> {
     pub fn root<'a>(&'a self) -> &'a VNode<'a> {
         &self.frames.cur_frame().head_node
         &self.frames.cur_frame().head_node
     }
     }

+ 0 - 116
packages/core/src/styles.rs

@@ -1,116 +0,0 @@
-//! Dedicated styling system for Components
-//! ---------------------------------------
-//!
-//! In the future, we'd like to move this out of Dioxus core or build a better, more general abstraction. For now, dedicated
-//! styling is more-or-less hardcoded into Dioxus.
-//!
-//!
-//!
-//!
-//!
-//!
-//!
-//!
-
-use crate::{innerlude::Attribute, nodebuilder::NodeFactory};
-
-pub struct StyleBuilder;
-pub trait AsAttr<'a> {
-    fn to_attr(self, field: &'static str, fac: &NodeFactory<'a>) -> Attribute<'a>;
-}
-impl<'a> AsAttr<'a> for std::fmt::Arguments<'a> {
-    fn to_attr(self, field: &'static str, fac: &NodeFactory<'a>) -> Attribute<'a> {
-        fac.attr(field, self, Some("style"))
-    }
-}
-
-macro_rules! build_styles {
-    ($ ($name:ident: $lit:literal,)* ) => {
-        impl StyleBuilder {
-            $(
-                pub fn $name<'a>(f: &NodeFactory<'a>, args: impl AsAttr<'a>) -> Attribute<'a> {
-                    args.to_attr($lit, f)
-                }
-            )*
-        }
-    };
-}
-
-build_styles! {
-    background: "background",
-    background_attachment: "background-attachment",
-    background_color: "background-color",
-    background_image: "background-image",
-    background_position: "background-position",
-    background_repeat: "background-repeat",
-    border: "border",
-    border_bottom: "border-bottom",
-    border_bottom_color: "border-bottom-color",
-    border_bottom_style: "border-bottom-style",
-    border_bottom_width: "border-bottom-width",
-    border_color: "border-color",
-    border_left: "border-left",
-    border_left_color: "border-left-color",
-    border_left_style: "border-left-style",
-    border_left_width: "border-left-width",
-    border_right: "border-right",
-    border_right_color: "border-right-color",
-    border_right_style: "border-right-style",
-    border_right_width: "border-right-width",
-    border_style: "border-style",
-    border_top: "border-top",
-    border_top_color: "border-top-color",
-    border_top_style: "border-top-style",
-    border_top_width: "border-top-width",
-    border_width: "border-width",
-    clear: "clear",
-    clip: "clip",
-    color: "color",
-    cursor: "cursor",
-    display: "display",
-    filter: "filter",
-    css_float: "css-float",
-    font: "font",
-    font_family: "font-family",
-    font_size: "font-size",
-    font_variant: "font-variant",
-    font_weight: "font-weight",
-    height: "height",
-    left: "left",
-    letter_spacing: "letter-spacing",
-    line_height: "line-height",
-    list_style: "list-style",
-    list_style_image: "list-style-image",
-    list_style_position: "list-style-position",
-    list_style_type: "list-style-type",
-    margin: "margin",
-    margin_bottom: "margin-bottom",
-    margin_left: "margin-left",
-    margin_right: "margin-right",
-    margin_top: "margin-top",
-    overflow: "overflow",
-    padding: "padding",
-    padding_bottom: "padding-bottom",
-    padding_left: "padding-left",
-    padding_right: "padding-right",
-    padding_top: "padding-top",
-    page_break_after: "page-break-after",
-    page_break_before: "page-break-before",
-    position: "position",
-    stroke_dasharray: "stroke-dasharray",
-    stroke_dashoffset: "stroke-dashoffset",
-    text_align: "text-align",
-    text_decoration: "text-decoration",
-    text_indent: "text-indent",
-    text_transform: "text-transform",
-    top: "top",
-    vertical_align: "vertical-align",
-    visibility: "visibility",
-    width: "width",
-    z_index: "z-index",
-}
-
-fn example(f: &NodeFactory) {
-    let style_list = &[("width", "10"), ("text-decoration", "")];
-    let styles = &[StyleBuilder::background(f, format_args!("10"))];
-}

+ 62 - 57
packages/core/src/tasks.rs

@@ -16,28 +16,28 @@ use std::{
     task::{Context, Poll},
     task::{Context, Poll},
 };
 };
 
 
-use futures_util::{Future, Stream, StreamExt};
+use futures_util::{stream::FuturesUnordered, Future, Stream, StreamExt};
 use slotmap::{DefaultKey, SlotMap};
 use slotmap::{DefaultKey, SlotMap};
 
 
-use crate::{events::EventTrigger, innerlude::ScopeIdx};
+use crate::innerlude::{EventTrigger, FiberTask, ScopeIdx};
 
 
-pub type TaskSubmitter = Arc<dyn Fn(DTask)>;
+pub type TaskSubmitter = Arc<dyn Fn(FiberTask)>;
 
 
 pub struct TaskQueue {
 pub struct TaskQueue {
-    slots: Arc<RwLock<SlotMap<DefaultKey, DTask>>>,
+    slots: Arc<RwLock<FuturesUnordered<FiberTask>>>,
+    // slots: Arc<RwLock<SlotMap<DefaultKey, DTask>>>,
     submitter: TaskSubmitter,
     submitter: TaskSubmitter,
 }
 }
 
 
 impl TaskQueue {
 impl TaskQueue {
     pub fn new() -> Self {
     pub fn new() -> Self {
-        let slots = Arc::new(RwLock::new(SlotMap::new()));
-
+        let slots = Arc::new(RwLock::new(FuturesUnordered::new()));
         let slots2 = slots.clone();
         let slots2 = slots.clone();
 
 
         let submitter = Arc::new(move |task| {
         let submitter = Arc::new(move |task| {
             let mut slots = slots2.write().unwrap();
             let mut slots = slots2.write().unwrap();
             log::debug!("Task submitted into global task queue");
             log::debug!("Task submitted into global task queue");
-            slots.insert(task);
+            slots.push(task);
         });
         });
         Self { slots, submitter }
         Self { slots, submitter }
     }
     }
@@ -46,9 +46,9 @@ impl TaskQueue {
         self.submitter.clone()
         self.submitter.clone()
     }
     }
 
 
-    pub fn submit_task(&mut self, task: DTask) -> TaskHandle {
-        let key = self.slots.write().unwrap().insert(task);
-        TaskHandle { key }
+    pub fn submit_task(&mut self, task: FiberTask) {
+        self.slots.write().unwrap().push(task);
+        // TaskHandle { key }
     }
     }
 
 
     pub fn is_empty(&self) -> bool {
     pub fn is_empty(&self) -> bool {
@@ -57,73 +57,78 @@ impl TaskQueue {
     pub fn len(&self) -> usize {
     pub fn len(&self) -> usize {
         self.slots.read().unwrap().len()
         self.slots.read().unwrap().len()
     }
     }
-}
-
-impl Stream for TaskQueue {
-    type Item = EventTrigger;
-
-    /// We can never be finished polling
-    fn poll_next(
-        mut self: Pin<&mut Self>,
-        cx: &mut std::task::Context<'_>,
-    ) -> std::task::Poll<Option<Self::Item>> {
-        // let yield_every = self.len();
-        // let mut polled = 0;
 
 
+    pub async fn next(&mut self) -> Option<EventTrigger> {
         let mut slots = self.slots.write().unwrap();
         let mut slots = self.slots.write().unwrap();
-        for (key, slot) in slots.iter_mut() {
-            if slot.dead.get() {
-                continue;
-            }
-            let r = slot.fut;
-            let mut fut = unsafe { &mut *r };
-            // use futures::{future::Future, poll, FutureExt};
-
-            let f2 = fut.as_mut();
-            let w = cx.waker();
-            let mut cx = Context::from_waker(&w);
-
-            // Pin::new_unchecked(pointer)
-            // use std::future::Future;
-            match f2.poll(&mut cx) {
-                Poll::Ready(_) => {
-                    let trigger = EventTrigger::new_from_task(slot.originator);
-                    slot.dead.set(true);
-                    return Poll::Ready(Some(trigger));
-                }
-                Poll::Pending => continue,
-            }
-        }
-
-        // we tried polling every active task.
-        // give up and relinquish controlto the parent
-
-        // We have polled a large number of futures in a row without yielding.
-        // To ensure we do not starve other tasks waiting on the executor,
-        // we yield here, but immediately wake ourselves up to continue.
-        // cx.waker().wake_by_ref();
-        return Poll::Pending;
+        slots.next().await
     }
     }
 }
 }
 
 
+// impl Stream for TaskQueue {
+//     type Item = EventTrigger;
+
+//     /// We can never be finished polling
+//     fn poll_next(
+//         self: Pin<&mut Self>,
+//         cx: &mut std::task::Context<'_>,
+//     ) -> std::task::Poll<Option<Self::Item>> {
+//         // let yield_every = self.len();
+//         // let mut polled = 0;
+
+//         let mut slots = self.slots.write().unwrap();
+//         for (_key, slot) in slots.iter_mut() {
+//             if slot.dead.get() {
+//                 continue;
+//             }
+//             let r = slot.fut;
+//             // let fut = unsafe { &mut *r };
+//             // use futures::{future::Future, poll, FutureExt};
+
+//             let f2 = fut.as_mut();
+//             let w = cx.waker();
+//             let mut cx = Context::from_waker(&w);
+
+//             // Pin::new_unchecked(pointer)
+//             // use std::future::Future;
+//             match f2.poll(&mut cx) {
+//                 Poll::Ready(_) => {
+//                     let trigger = EventTrigger::new_from_task(slot.originator);
+//                     slot.dead.set(true);
+//                     return Poll::Ready(Some(trigger));
+//                 }
+//                 Poll::Pending => continue,
+//             }
+//         }
+
+//         // we tried polling every active task.
+//         // give up and relinquish controlto the parent
+
+//         // We have polled a large number of futures in a row without yielding.
+//         // To ensure we do not starve other tasks waiting on the executor,
+//         // we yield here, but immediately wake ourselves up to continue.
+//         // cx.waker().wake_by_ref();
+//         return Poll::Pending;
+//     }
+// }
+
 pub struct TaskHandle {
 pub struct TaskHandle {
     key: DefaultKey,
     key: DefaultKey,
 }
 }
 
 
 pub struct DTask {
 pub struct DTask {
-    fut: *mut Pin<Box<dyn Future<Output = ()>>>,
+    fut: FiberTask,
     originator: ScopeIdx,
     originator: ScopeIdx,
     dead: Cell<bool>,
     dead: Cell<bool>,
 }
 }
 impl DTask {
 impl DTask {
-    pub fn new(fut: &mut Pin<Box<dyn Future<Output = ()>>>, originator: ScopeIdx) -> Self {
+    pub fn new(fut: FiberTask, originator: ScopeIdx) -> Self {
         Self {
         Self {
             fut,
             fut,
             originator,
             originator,
             dead: Cell::new(false),
             dead: Cell::new(false),
         }
         }
     }
     }
-    pub fn debug_new(fut: &mut Pin<Box<dyn Future<Output = ()>>>) -> Self {
+    pub fn debug_new(fut: FiberTask) -> Self {
         let originator = ScopeIdx::default();
         let originator = ScopeIdx::default();
         Self {
         Self {
             fut,
             fut,

+ 17 - 15
packages/core/src/util.rs

@@ -1,7 +1,6 @@
 use std::{
 use std::{
-    cell::{RefCell, RefMut},
+    cell::{Cell, RefCell, RefMut},
     rc::Rc,
     rc::Rc,
-    vec::Drain,
 };
 };
 
 
 use futures_util::StreamExt;
 use futures_util::StreamExt;
@@ -65,6 +64,9 @@ impl RealDomNode {
     pub const fn empty() -> Self {
     pub const fn empty() -> Self {
         Self(u64::MIN)
         Self(u64::MIN)
     }
     }
+    pub const fn empty_cell() -> Cell<Self> {
+        Cell::new(Self::empty())
+    }
 }
 }
 
 
 pub struct DebugDom {
 pub struct DebugDom {
@@ -86,23 +88,23 @@ impl DebugDom {
     }
     }
 }
 }
 impl<'a> RealDom<'a> for DebugDom {
 impl<'a> RealDom<'a> for DebugDom {
-    fn push(&mut self, root: RealDomNode) {}
+    fn push(&mut self, _root: RealDomNode) {}
     fn pop(&mut self) {}
     fn pop(&mut self) {}
 
 
-    fn append_children(&mut self, many: u32) {}
+    fn append_children(&mut self, _many: u32) {}
 
 
-    fn replace_with(&mut self, many: u32) {}
+    fn replace_with(&mut self, _many: u32) {}
 
 
     fn remove(&mut self) {}
     fn remove(&mut self) {}
 
 
     fn remove_all_children(&mut self) {}
     fn remove_all_children(&mut self) {}
 
 
-    fn create_text_node(&mut self, text: &str) -> RealDomNode {
+    fn create_text_node(&mut self, _text: &str) -> RealDomNode {
         self.counter += 1;
         self.counter += 1;
         RealDomNode::new(self.counter)
         RealDomNode::new(self.counter)
     }
     }
 
 
-    fn create_element(&mut self, tag: &str, ns: Option<&'a str>) -> RealDomNode {
+    fn create_element(&mut self, _tag: &str, _ns: Option<&'a str>) -> RealDomNode {
         self.counter += 1;
         self.counter += 1;
         RealDomNode::new(self.counter)
         RealDomNode::new(self.counter)
     }
     }
@@ -114,20 +116,20 @@ impl<'a> RealDom<'a> for DebugDom {
 
 
     fn new_event_listener(
     fn new_event_listener(
         &mut self,
         &mut self,
-        event: &str,
-        scope: ScopeIdx,
-        element_id: usize,
-        realnode: RealDomNode,
+        _event: &str,
+        _scope: ScopeIdx,
+        _element_id: usize,
+        _realnode: RealDomNode,
     ) {
     ) {
     }
     }
 
 
-    fn remove_event_listener(&mut self, event: &str) {}
+    fn remove_event_listener(&mut self, _event: &str) {}
 
 
-    fn set_text(&mut self, text: &str) {}
+    fn set_text(&mut self, _text: &str) {}
 
 
-    fn set_attribute(&mut self, name: &str, value: &str, namespace: Option<&str>) {}
+    fn set_attribute(&mut self, _name: &str, _value: &str, _namespace: Option<&str>) {}
 
 
-    fn remove_attribute(&mut self, name: &str) {}
+    fn remove_attribute(&mut self, _name: &str) {}
 
 
     fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
     fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
         todo!()
         todo!()

+ 22 - 8
packages/core/src/virtual_dom.rs

@@ -21,13 +21,13 @@
 
 
 use crate::tasks::TaskQueue;
 use crate::tasks::TaskQueue;
 use crate::{arena::SharedArena, innerlude::*};
 use crate::{arena::SharedArena, innerlude::*};
-use appendlist::AppendList;
+
 use slotmap::DefaultKey;
 use slotmap::DefaultKey;
 use slotmap::SlotMap;
 use slotmap::SlotMap;
 use std::any::Any;
 use std::any::Any;
-use std::cell::RefCell;
+
+use std::any::TypeId;
 use std::pin::Pin;
 use std::pin::Pin;
-use std::{any::TypeId, fmt::Debug, rc::Rc};
 
 
 pub type ScopeIdx = DefaultKey;
 pub type ScopeIdx = DefaultKey;
 
 
@@ -146,7 +146,7 @@ impl VirtualDom {
             .with(|arena| {
             .with(|arena| {
                 arena.insert_with_key(move |myidx| {
                 arena.insert_with_key(move |myidx| {
                     let event_channel = _event_queue.new_channel(0, myidx);
                     let event_channel = _event_queue.new_channel(0, myidx);
-                    let caller = crate::nodes::create_component_caller(root, props_ptr as *const _);
+                    let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
                     Scope::new(caller, myidx, None, 0, event_channel, link, &[], submitter)
                     Scope::new(caller, myidx, None, 0, event_channel, link, &[], submitter)
                 })
                 })
             })
             })
@@ -191,7 +191,10 @@ impl VirtualDom {
     }
     }
 
 
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
+    ///
     /// Currently this doesn't do what we want it to do
     /// Currently this doesn't do what we want it to do
+    ///
+    /// The diff machine expects the RealDom's stack to be the root of the application
     pub fn rebuild<'s, Dom: RealDom<'s>>(&'s mut self, realdom: &mut Dom) -> Result<()> {
     pub fn rebuild<'s, Dom: RealDom<'s>>(&'s mut self, realdom: &mut Dom) -> Result<()> {
         let mut diff_machine = DiffMachine::new(
         let mut diff_machine = DiffMachine::new(
             realdom,
             realdom,
@@ -201,15 +204,26 @@ impl VirtualDom {
             &self.tasks,
             &self.tasks,
         );
         );
 
 
+        let cur_component = self.components.try_get_mut(self.base_scope).unwrap();
+
+        cur_component.run_scope()?;
+
+        let meta = diff_machine.create(cur_component.next_frame());
+        log::info!(
+            "nodes created! appending to body {:#?}",
+            meta.added_to_stack
+        );
+        diff_machine.dom.append_children(meta.added_to_stack);
+
         // Schedule an update and then immediately call it on the root component
         // Schedule an update and then immediately call it on the root component
         // This is akin to a hook being called from a listener and requring a re-render
         // This is akin to a hook being called from a listener and requring a re-render
         // Instead, this is done on top-level component
         // Instead, this is done on top-level component
-        let base = self.components.try_get(self.base_scope)?;
+        // let base = self.components.try_get(self.base_scope)?;
 
 
-        let update = &base.event_channel;
-        update();
+        // let update = &base.event_channel;
+        // update();
 
 
-        self.progress_completely(&mut diff_machine)?;
+        // self.progress_completely(&mut diff_machine)?;
 
 
         Ok(())
         Ok(())
     }
     }

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

@@ -1,4 +1,4 @@
-use dioxus_core::prelude::*;
+
 // type VirtualNode = VNode;
 // type VirtualNode = VNode;
 
 
 /// Test a basic usage of a virtual dom + text renderer combo
 /// Test a basic usage of a virtual dom + text renderer combo

+ 7 - 2
packages/hooks/src/usestate.rs

@@ -57,7 +57,7 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
         move || UseStateInner {
         move || UseStateInner {
             current_val: initial_state_fn(),
             current_val: initial_state_fn(),
             callback: cx.schedule_update(),
             callback: cx.schedule_update(),
-            wip: RefCell::new(None),
+            wip: Rc::new(RefCell::new(None)),
             update_scheuled: Cell::new(false),
             update_scheuled: Cell::new(false),
         },
         },
         move |hook| {
         move |hook| {
@@ -76,7 +76,7 @@ struct UseStateInner<T: 'static> {
     current_val: T,
     current_val: T,
     update_scheuled: Cell<bool>,
     update_scheuled: Cell<bool>,
     callback: Rc<dyn Fn()>,
     callback: Rc<dyn Fn()>,
-    wip: RefCell<Option<T>>,
+    wip: Rc<RefCell<Option<T>>>,
 }
 }
 
 
 pub struct UseState<'a, T: 'static> {
 pub struct UseState<'a, T: 'static> {
@@ -118,6 +118,11 @@ impl<'a, T: 'static> UseState<'a, T> {
     pub fn classic(self) -> (&'a T, &'a Rc<dyn Fn(T)>) {
     pub fn classic(self) -> (&'a T, &'a Rc<dyn Fn(T)>) {
         todo!()
         todo!()
     }
     }
+
+    pub fn setter(&self) -> Rc<dyn Fn(T)> {
+        let slot = self.inner.wip.clone();
+        Rc::new(move |new| *slot.borrow_mut() = Some(new))
+    }
 }
 }
 impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
 impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
     pub fn get_mut(self) -> RefMut<'a, T> {
     pub fn get_mut(self) -> RefMut<'a, T> {

+ 86 - 1
packages/html/README.md

@@ -1,6 +1,6 @@
 # Html (and SVG) Namespace for Dioxus
 # Html (and SVG) Namespace for Dioxus
 
 
-The Dioxus `rsx!` and `html!` macros can accept any compile-time correct namespace on top of NodeBuilder. This crate provides the HTML (and SVG) namespaces which get imported in the Dioxus prelude.
+The Dioxus `rsx!` and `html!` macros can accept any compile-time correct namespace on top of NodeFactory. This crate provides the HTML (and SVG) namespaces which get imported in the Dioxus prelude.
 
 
 However, this abstraction enables you to add any namespace of elements, provided they're in scope when rsx! is called. For an example, a UI that is designed for Augmented Reality might use different primitives than HTML:
 However, this abstraction enables you to add any namespace of elements, provided they're in scope when rsx! is called. For an example, a UI that is designed for Augmented Reality might use different primitives than HTML:
 
 
@@ -20,3 +20,88 @@ rsx! {
 ```
 ```
 
 
 This is currently a not-very-explored part of Dioxus. However, the namespacing system does make it possible to provide syntax highlighting, documentation, "go to definition" and compile-time correctness, so it's worth having it abstracted.
 This is currently a not-very-explored part of Dioxus. However, the namespacing system does make it possible to provide syntax highlighting, documentation, "go to definition" and compile-time correctness, so it's worth having it abstracted.
+
+## How it works:
+
+Elements for dioxus must implement the (simple) DioxusElement trait to be used in the rsx! macro.
+
+```rust
+struct div;
+impl DioxusElement for div {
+    const TAG_NAME: &'static str = "div";
+    const NAME_SPACE: Option<&'static str> = None;
+}
+```
+
+All elements should be defined as a zero-sized-struct (also known as unit struct). These structs are zero-cost and just provide the type-level trickery to Rust for compile-time correct templates.
+
+Attributes would then be implemented as methods on these unit structs.
+
+The HTML namespace is defined mostly with macros. However, the expanded form would look something like this:
+```rust
+struct base;
+impl DioxusElement for base {
+    const TAG_NAME: &'static str = "base";
+    const NAME_SPACE: Option<&'static str> = None;    
+}
+impl base {
+    #[inline]
+    fn href<'a>(&self, f: NodeFactory<'a>, v: Arguments) -> Attribute<'a> {
+        f.attr("href", v, None, false)
+    }
+    #[inline]
+    fn target<'a>(&self, f: NodeFactory<'a>, v: Arguments) -> Attribute<'a> {
+        f.attr("target", v, None, false)
+    }
+}
+```
+Because attributes are defined as methods on the unit struct, they guard the attribute creation behind a compile-time correct interface.
+
+
+## How to extend it:
+
+Whenever the rsx! macro is called, it relies on a module `dioxus_elements` to be in scope. When you enable the `html` feature in dioxus, this module gets imported in the prelude. However, you can extend this with your own set of custom elements by making your own `dioxus_elements` module and re-exporting the html namespace. 
+
+```rust
+mod dioxus_elements {
+    use dioxus::prelude::dioxus_elements::*;
+    struct my_element;
+    impl DioxusElement for my_element {
+        const TAG_NAME: &'static str = "base";
+        const NAME_SPACE: Option<&'static str> = None;            
+    }
+}
+```
+
+## Limitations:
+- 
+
+## How to work around it:
+If an attribute in Dioxus is invalid (defined incorrectly) - first, make an issue - but then, you can work around it. The raw builder API is actually somewhat ergonomic to work with, and the NodeFactory type exposes a bunch of methods to make any type of tree - even invalid ones! So obviously, be careful, but there's basically anything you can do.
+
+```rust
+cx.render(rsx!{
+    div {
+        h1 {}
+        // Oh no! I need a super custom element
+        {LazyNodes::new(move |f| {
+            f.raw_element(
+                // tag name
+                "custom_element", 
+
+                // attributes
+                &[f.attr("billy", format_args!("goat"))], 
+
+                // listeners
+                &[f.listener(onclick(move |_| {}))], 
+
+                // children
+                &[cx.render(rsx!(div {} ))], 
+
+                // key
+                None 
+            )
+        })}
+    }
+})
+```

+ 922 - 113
packages/html/src/lib.rs

@@ -6,11 +6,267 @@
 //! An added benefit of this approach is the ability to lend comprehensive documentation on how to use these elements inside
 //! An added benefit of this approach is the ability to lend comprehensive documentation on how to use these elements inside
 //! of the Rsx and Html macros. Each element comes with a substantial amount of documentation on how to best use it, hopefully
 //! of the Rsx and Html macros. Each element comes with a substantial amount of documentation on how to best use it, hopefully
 //! making the development cycle quick.
 //! making the development cycle quick.
+//!
+//! All elements are used as zero-sized unit structs with trait impls.
+//!
+//! 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,
+        )*
+    ) => {
+        $(
+            $(#[$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;
 
 
-use dioxus_core::{DioxusElement, NodeFactory};
+        /// 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;
+    }
+    style_trait_methods! {
+        background: "background",
+        background_attachment: "background-attachment",
+
+        /// ## Definition and Usage
+        ///
+        /// The background-color property sets the background color of an element.
+        ///
+        /// The background of an element is the total size of the element, including padding and border (but not the margin).
+        ///
+        /// Tip: Use a background color and a text color that makes the text easy to read.
+        ///
+        /// ## Example
+        ///
+        /// ```
+        /// body {
+        ///     style: {
+        ///         background_color: "coral"
+        ///     }
+        /// }
+        /// ```
+        background_color: "background-color",
+        background_image: "background-image",
+        background_position: "background-position",
+        background_repeat: "background-repeat",
+        border: "border",
+        border_bottom: "border-bottom",
+        border_bottom_color: "border-bottom-color",
+        border_bottom_style: "border-bottom-style",
+        border_bottom_width: "border-bottom-width",
+        border_color: "border-color",
+        border_left: "border-left",
+        border_left_color: "border-left-color",
+        border_left_style: "border-left-style",
+        border_left_width: "border-left-width",
+        border_right: "border-right",
+        border_right_color: "border-right-color",
+        border_right_style: "border-right-style",
+        border_right_width: "border-right-width",
+        border_style: "border-style",
+        border_top: "border-top",
+        border_top_color: "border-top-color",
+        border_top_style: "border-top-style",
+        border_top_width: "border-top-width",
+        border_width: "border-width",
+        clear: "clear",
+        clip: "clip",
+        color: "color",
+        cursor: "cursor",
+        display: "display",
+        filter: "filter",
+        css_float: "css-float",
+        font: "font",
+        font_family: "font-family",
+        font_size: "font-size",
+        font_variant: "font-variant",
+        font_weight: "font-weight",
+        height: "height",
+        left: "left",
+        letter_spacing: "letter-spacing",
+        line_height: "line-height",
+        list_style: "list-style",
+        list_style_image: "list-style-image",
+        list_style_position: "list-style-position",
+        list_style_type: "list-style-type",
+        margin: "margin",
+        margin_bottom: "margin-bottom",
+        margin_left: "margin-left",
+        margin_right: "margin-right",
+        margin_top: "margin-top",
+        overflow: "overflow",
+        padding: "padding",
+        padding_bottom: "padding-bottom",
+        padding_left: "padding-left",
+        padding_right: "padding-right",
+        padding_top: "padding-top",
+        page_break_after: "page-break-after",
+        page_break_before: "page-break-before",
+        position: "position",
+        stroke_dasharray: "stroke-dasharray",
+        stroke_dashoffset: "stroke-dashoffset",
+        text_align: "text-align",
+        text_decoration: "text-decoration",
+        text_indent: "text-indent",
+        text_transform: "text-transform",
+        top: "top",
+        vertical_align: "vertical-align",
+        visibility: "visibility",
+        width: "width",
+        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 {
 macro_rules! builder_constructors {
-    ( $( $(#[$attr:meta])* $name:ident; )* ) => {
+    (
+        $(
+            $(#[$attr:meta])*
+            $name:ident {
+                $($fil:ident: $vil:ident,)*
+            };
+         )*
+    ) => {
         $(
         $(
             #[allow(non_camel_case_types)]
             #[allow(non_camel_case_types)]
             $(#[$attr])*
             $(#[$attr])*
@@ -20,6 +276,16 @@ macro_rules! builder_constructors {
                 const TAG_NAME: &'static str = stringify!($name);
                 const TAG_NAME: &'static str = stringify!($name);
                 const NAME_SPACE: Option<&'static str> = None;
                 const NAME_SPACE: Option<&'static str> = None;
             }
             }
+
+            impl GlobalAttributes for $name {}
+
+            impl $name {
+                $(
+                    pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+                        cx.attr(stringify!($fil), val, None, false)
+                    }
+                )*
+            }
         )*
         )*
     };
     };
 
 
@@ -44,7 +310,7 @@ macro_rules! builder_constructors {
 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
 // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
 //
 //
 // Does not include obsolete elements.
 // Does not include obsolete elements.
-
+//
 // This namespace represents a collection of modern HTML-5 compatiable elements.
 // This namespace represents a collection of modern HTML-5 compatiable elements.
 //
 //
 // This list does not include obsolete, deprecated, experimental, or poorly supported elements.
 // This list does not include obsolete, deprecated, experimental, or poorly supported elements.
@@ -55,63 +321,91 @@ builder_constructors! {
     /// [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
     /// [`<base>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base)
     /// element.
     /// element.
     ///
     ///
-    /// A base element is your mom!
-    base;
+    base {
+        href: Uri,
+        target: Target,
+    };
 
 
     /// Build a
     /// Build a
     /// [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
     /// [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
     /// element.
     /// element.
-    head;
+    head {};
 
 
     /// Build a
     /// Build a
     /// [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
     /// [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
     /// element.
     /// element.
-    link;
+    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,
+    };
 
 
     /// Build a
     /// Build a
     /// [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
     /// [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
     /// element.
     /// element.
-    meta;
+    meta {
+        charset: String, // FIXME IANA standard names
+        content: String,
+        http_equiv: HTTPEquiv,
+        name: Metadata,
+    };
 
 
     /// Build a
     /// Build a
     /// [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
     /// [`<style>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
     /// element.
     /// element.
-    style;
+    style {
+        r#type: Mime,
+        media: String, // FIXME media query
+        nonce: Nonce,
+        title: String, // FIXME
+    };
 
 
     /// Build a
     /// Build a
     /// [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
     /// [`<title>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title)
     /// element.
     /// element.
-    title;
+    title { };
 
 
     // Sectioning root
     // Sectioning root
 
 
     /// Build a
     /// Build a
     /// [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
     /// [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
     /// element.
     /// element.
-    body;
+    body {};
 
 
+    // ------------------
     // Content sectioning
     // Content sectioning
+    // ------------------
 
 
     /// Build a
     /// Build a
     /// [`<address>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address)
     /// [`<address>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address)
     /// element.
     /// element.
-    address;
+    address {};
+
     /// Build a
     /// Build a
     /// [`<article>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article)
     /// [`<article>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article)
     /// element.
     /// element.
-    article;
+    article {};
+
     /// Build a
     /// Build a
     /// [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
     /// [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside)
     /// element.
     /// element.
-    aside;
+    aside {};
+
     /// Build a
     /// Build a
     /// [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)
     /// [`<footer>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer)
     /// element.
     /// element.
-    footer;
+    footer {};
+
     /// Build a
     /// Build a
     /// [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)
     /// [`<header>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header)
     /// element.
     /// element.
-    header;
+    header {};
 
 
     /// Build a
     /// Build a
     /// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
     /// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
@@ -131,8 +425,7 @@ builder_constructors! {
     /// rsx!(h1 { "A header element" })
     /// rsx!(h1 { "A header element" })
     /// LazyNodes::new(|f| f.el(h1).children([f.text("A header element")]).finish())
     /// LazyNodes::new(|f| f.el(h1).children([f.text("A header element")]).finish())
     /// ```
     /// ```
-    ///
-    h1;
+    h1 {};
 
 
 
 
     /// Build a
     /// Build a
@@ -152,7 +445,7 @@ builder_constructors! {
     /// rsx!(h2 { "A header element" })
     /// rsx!(h2 { "A header element" })
     /// LazyNodes::new(|f| f.el(h2).children([f.text("A header element")]).finish())
     /// LazyNodes::new(|f| f.el(h2).children([f.text("A header element")]).finish())
     /// ```
     /// ```
-    h2;
+    h2 {};
 
 
 
 
     /// Build a
     /// Build a
@@ -165,416 +458,758 @@ builder_constructors! {
     /// - The most important heading is <h1> and the least important heading is <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 the first heading in the document.
     /// - The <h1> heading is usually a large bolded font.
     /// - The <h1> heading is usually a large bolded font.
-    h3;
+    h3 {};
     /// Build a
     /// Build a
     /// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
     /// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
     /// element.
     /// element.
-    h4;
+    h4 {};
     /// Build a
     /// Build a
     /// [`<h5>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5)
     /// [`<h5>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5)
     /// element.
     /// element.
-    h5;
+    h5 {};
     /// Build a
     /// Build a
     /// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
     /// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
     /// element.
     /// element.
-    h6;
+    h6 {};
 
 
     /// Build a
     /// Build a
     /// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
     /// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
     /// element.
     /// element.
-    main;
+    main {};
     /// Build a
     /// Build a
     /// [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)
     /// [`<nav>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav)
     /// element.
     /// element.
-    nav;
+    nav {};
     /// Build a
     /// Build a
     /// [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)
     /// [`<section>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section)
     /// element.
     /// element.
-    section;
+    section {};
 
 
     // Text content
     // Text content
 
 
     /// Build a
     /// Build a
     /// [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)
     /// [`<blockquote>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote)
     /// element.
     /// element.
-    blockquote;
+    blockquote {
+        cite: Uri,
+    };
     /// Build a
     /// Build a
     /// [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)
     /// [`<dd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd)
     /// element.
     /// element.
-    dd;
+    dd {};
+
     /// Build a
     /// Build a
     /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
     /// [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div)
     /// element.
     /// element.
-    div;
+    ///
+    /// ## 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
     /// Build a
     /// [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)
     /// [`<dl>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)
     /// element.
     /// element.
-    dl;
+    dl {};
+
     /// Build a
     /// Build a
     /// [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)
     /// [`<dt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt)
     /// element.
     /// element.
-    dt;
+    dt {};
+
     /// Build a
     /// Build a
     /// [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)
     /// [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption)
     /// element.
     /// element.
-    figcaption;
+    figcaption {};
+
     /// Build a
     /// Build a
     /// [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)
     /// [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure)
     /// element.
     /// element.
-    figure;
+    figure {};
+
     /// Build a
     /// Build a
     /// [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)
     /// [`<hr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)
     /// element.
     /// element.
-    hr;
+    hr {};
+
     /// Build a
     /// Build a
     /// [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
     /// [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
     /// element.
     /// element.
-    li;
+    li {
+        value: isize,
+    };
+
     /// Build a
     /// Build a
     /// [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)
     /// [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)
     /// element.
     /// element.
-    ol;
+    ol {
+        reversed: Bool,
+        start: isize,
+        r#type: OrderedListType,
+    };
+
     /// Build a
     /// Build a
     /// [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)
     /// [`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)
     /// element.
     /// element.
-    p;
+    p {};
+
     /// Build a
     /// Build a
     /// [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)
     /// [`<pre>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre)
     /// element.
     /// element.
-    pre;
+    pre {};
+
     /// Build a
     /// Build a
     /// [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
     /// [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
     /// element.
     /// element.
-    ul;
+    ul {};
+
 
 
     // Inline text semantics
     // Inline text semantics
 
 
     /// Build a
     /// Build a
     /// [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
     /// [`<a>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
     /// element.
     /// element.
-    a;
+    a {
+        download: String,
+        href: Uri,
+        hreflang: LanguageTag,
+        target: Target,
+        r#type: Mime,
+        // ping: SpacedList<Uri>,
+        // rel: SpacedList<LinkType>,
+    };
+
     /// Build a
     /// Build a
     /// [`<abbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr)
     /// [`<abbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr)
     /// element.
     /// element.
-    abbr;
+    abbr {};
+
     /// Build a
     /// Build a
     /// [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)
     /// [`<b>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b)
     /// element.
     /// element.
-    b;
+    b {};
+
     /// Build a
     /// Build a
     /// [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)
     /// [`<bdi>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdi)
     /// element.
     /// element.
-    bdi;
+    bdi {};
+
     /// Build a
     /// Build a
     /// [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)
     /// [`<bdo>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo)
     /// element.
     /// element.
-    bdo;
+    bdo {};
+
     /// Build a
     /// Build a
     /// [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)
     /// [`<br>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br)
     /// element.
     /// element.
-    br;
+    br {};
+
     /// Build a
     /// Build a
     /// [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)
     /// [`<cite>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite)
     /// element.
     /// element.
-    cite;
+    cite {};
+
     /// Build a
     /// Build a
     /// [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)
     /// [`<code>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code)
     /// element.
     /// element.
-    code;
+    code {};
+
     /// Build a
     /// Build a
     /// [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)
     /// [`<data>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data)
     /// element.
     /// element.
-    data;
+    data {
+        value: String,
+    };
+
     /// Build a
     /// Build a
     /// [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)
     /// [`<dfn>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn)
     /// element.
     /// element.
-    dfn;
+    dfn {};
+
     /// Build a
     /// Build a
     /// [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)
     /// [`<em>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em)
     /// element.
     /// element.
-    em;
+    em {};
+
     /// Build a
     /// Build a
     /// [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)
     /// [`<i>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i)
     /// element.
     /// element.
-    i;
+    i {};
+
     /// Build a
     /// Build a
     /// [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)
     /// [`<kbd>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd)
     /// element.
     /// element.
-    kbd;
+    kbd {};
+
     /// Build a
     /// Build a
     /// [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)
     /// [`<mark>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark)
     /// element.
     /// element.
-    mark;
+    mark {};
+
     /// Build a
     /// Build a
     /// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
     /// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
     /// element.
     /// element.
-    q;
+    q {
+        cite: Uri,
+    };
+
 
 
     /// Build a
     /// Build a
     /// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
     /// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
     /// element.
     /// element.
-    rp;
+    rp {};
+
 
 
     /// Build a
     /// Build a
     /// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
     /// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
     /// element.
     /// element.
-    rt;
+    rt {};
+
 
 
     /// Build a
     /// Build a
     /// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
     /// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
     /// element.
     /// element.
-    ruby;
+    ruby {};
+
     /// Build a
     /// Build a
     /// [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)
     /// [`<s>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s)
     /// element.
     /// element.
-    s;
+    s {};
+
     /// Build a
     /// Build a
     /// [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)
     /// [`<samp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/samp)
     /// element.
     /// element.
-    samp;
+    samp {};
+
     /// Build a
     /// Build a
     /// [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)
     /// [`<small>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small)
     /// element.
     /// element.
-    small;
+    small {};
+
     /// Build a
     /// Build a
     /// [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)
     /// [`<span>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)
     /// element.
     /// element.
-    span;
+    span {};
+
     /// Build a
     /// Build a
     /// [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)
     /// [`<strong>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong)
     /// element.
     /// element.
-    strong;
+    strong {};
+
     /// Build a
     /// Build a
     /// [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)
     /// [`<sub>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub)
     /// element.
     /// element.
-    sub;
+    sub {};
+
     /// Build a
     /// Build a
     /// [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)
     /// [`<sup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup)
     /// element.
     /// element.
-    sup;
+    sup {};
+
     /// Build a
     /// Build a
     /// [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)
     /// [`<time>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time)
     /// element.
     /// element.
-    time;
+    time {};
+
     /// Build a
     /// Build a
     /// [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)
     /// [`<u>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u)
     /// element.
     /// element.
-    u;
+    u {};
+
     /// Build a
     /// Build a
     /// [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)
     /// [`<var>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var)
     /// element.
     /// element.
-    var;
+    var {};
+
     /// Build a
     /// Build a
     /// [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)
     /// [`<wbr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr)
     /// element.
     /// element.
-    wbr;
+    wbr {};
+
 
 
     // Image and multimedia
     // Image and multimedia
 
 
     /// Build a
     /// Build a
     /// [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
     /// [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
     /// element.
     /// element.
-    area;
+    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
     /// Build a
     /// [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
     /// [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
     /// element.
     /// element.
-    audio;
+    audio {
+        autoplay: Bool,
+        controls: Bool,
+        crossorigin: CrossOrigin,
+        muted: Bool,
+        preload: Preload,
+        src: Uri,
+        r#loop: Bool,
+    };
+
     /// Build a
     /// Build a
     /// [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
     /// [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img)
     /// element.
     /// element.
-    img;
+    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,
+        // sizes: SpacedList<String>, // FIXME it's not really just a string
+    };
+
     /// Build a
     /// Build a
     /// [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)
     /// [`<map>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map)
     /// element.
     /// element.
-    map;
+    map {
+        name: Id,
+    };
+
     /// Build a
     /// Build a
     /// [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)
     /// [`<track>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track)
     /// element.
     /// element.
-    track;
+    track {
+        default: Bool,
+        kind: VideoKind,
+        label: String,
+        src: Uri,
+        srclang: LanguageTag,
+    };
+
     /// Build a
     /// Build a
     /// [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
     /// [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)
     /// element.
     /// element.
-    video;
+    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
     // Embedded content
 
 
     /// Build a
     /// Build a
     /// [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)
     /// [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed)
     /// element.
     /// element.
-    embed;
+    embed {
+        height: usize,
+        src: Uri,
+        r#type: Mime,
+        width: usize,
+    };
+
     /// Build a
     /// Build a
     /// [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
     /// [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
     /// element.
     /// element.
-    iframe;
+    iframe {
+        allow: FeaturePolicy,
+        allowfullscreen: Bool,
+        allowpaymentrequest: Bool,
+        height: usize,
+        name: Id,
+        referrerpolicy: ReferrerPolicy,
+        src: Uri,
+        srcdoc: Uri,
+        width: usize,
+        // sandbox: SpacedSet<Sandbox>,
+    };
+
     /// Build a
     /// Build a
     /// [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)
     /// [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object)
     /// element.
     /// element.
-    object;
+    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
     /// Build a
     /// [`<param>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param)
     /// [`<param>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param)
     /// element.
     /// element.
-    param;
+    param {
+        name: String,
+        value: String,
+    };
+
     /// Build a
     /// Build a
     /// [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
     /// [`<picture>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture)
     /// element.
     /// element.
-    picture;
+    picture {};
+
     /// Build a
     /// Build a
     /// [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)
     /// [`<source>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source)
     /// element.
     /// element.
-    source;
+    source {
+        src: Uri,
+        r#type: Mime,
+    };
+
 
 
     // Scripting
     // Scripting
 
 
     /// Build a
     /// Build a
     /// [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
     /// [`<canvas>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas)
     /// element.
     /// element.
-    canvas;
+    canvas {
+        height: usize,
+        width: usize,
+    };
+
     /// Build a
     /// Build a
     /// [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)
     /// [`<noscript>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript)
     /// element.
     /// element.
-    noscript;
+    noscript {};
+
     /// Build a
     /// Build a
     /// [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
     /// [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
     /// element.
     /// element.
-    script;
+    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
     // Demarcating edits
 
 
     /// Build a
     /// Build a
     /// [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)
     /// [`<del>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del)
     /// element.
     /// element.
-    del;
+    del {
+        cite: Uri,
+        datetime: Datetime,
+    };
+
     /// Build a
     /// Build a
     /// [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)
     /// [`<ins>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins)
     /// element.
     /// element.
-    ins;
+    ins {
+        cite: Uri,
+        datetime: Datetime,
+    };
+
 
 
     // Table content
     // Table content
 
 
     /// Build a
     /// Build a
     /// [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)
     /// [`<caption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption)
     /// element.
     /// element.
-    caption;
+    caption {};
+
     /// Build a
     /// Build a
     /// [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)
     /// [`<col>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col)
     /// element.
     /// element.
-    col;
+    col {
+        span: usize,
+    };
+
     /// Build a
     /// Build a
     /// [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
     /// [`<colgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup)
     /// element.
     /// element.
-    colgroup;
+    colgroup {
+        span: usize,
+    };
+
     /// Build a
     /// Build a
     /// [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)
     /// [`<table>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table)
     /// element.
     /// element.
-    table;
+    table {};
+
     /// Build a
     /// Build a
     /// [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)
     /// [`<tbody>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody)
     /// element.
     /// element.
-    tbody;
+    tbody {};
+
     /// Build a
     /// Build a
     /// [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
     /// [`<td>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td)
     /// element.
     /// element.
-    td;
+    td {
+        colspan: usize,
+        rowspan: usize,
+        // headers: SpacedSet<Id>,
+    };
+
     /// Build a
     /// Build a
     /// [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)
     /// [`<tfoot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot)
     /// element.
     /// element.
-    tfoot;
+    tfoot {};
+
     /// Build a
     /// Build a
     /// [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)
     /// [`<th>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th)
     /// element.
     /// element.
-    th;
+    th {
+        abbr: String,
+        colspan: usize,
+        rowspan: usize,
+        scope: TableHeaderScope,
+        // headers: SpacedSet<Id>,
+    };
+
     /// Build a
     /// Build a
     /// [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)
     /// [`<thead>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead)
     /// element.
     /// element.
-    thead;
+    thead {};
+
     /// Build a
     /// Build a
     /// [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)
     /// [`<tr>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr)
     /// element.
     /// element.
-    tr;
+    tr {};
+
 
 
     // Forms
     // Forms
 
 
     /// Build a
     /// Build a
     /// [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
     /// [`<button>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button)
     /// element.
     /// element.
-    button;
+    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
     /// Build a
     /// [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)
     /// [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist)
     /// element.
     /// element.
-    datalist;
+    datalist {};
+
     /// Build a
     /// Build a
     /// [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)
     /// [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset)
     /// element.
     /// element.
-    fieldset;
+    fieldset {};
+
     /// Build a
     /// Build a
     /// [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
     /// [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
     /// element.
     /// element.
-    form;
+    form {
+        // accept-charset: SpacedList<CharacterEncoding>,
+        action: Uri,
+        autocomplete: OnOff,
+        enctype: FormEncodingType,
+        method: FormMethod,
+        name: Id,
+        novalidate: Bool,
+        target: Target,
+    };
+
     /// Build a
     /// Build a
     /// [`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
     /// [`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
     /// element.
     /// element.
-    input;
+    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,
+        r#type: InputType,
+        value: String,
+        width: isize,
+    };
+
     /// Build a
     /// Build a
     /// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
     /// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
     /// element.
     /// element.
-    label;
+    label {
+        r#for: Id,
+        form: Id,
+    };
+
     /// Build a
     /// Build a
     /// [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)
     /// [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)
     /// element.
     /// element.
-    legend;
+    legend {};
+
     /// Build a
     /// Build a
     /// [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)
     /// [`<meter>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter)
     /// element.
     /// element.
-    meter;
+    meter {
+        value: isize,
+        min: isize,
+        max: isize,
+        low: isize,
+        high: isize,
+        optimum: isize,
+        form: Id,
+    };
+
     /// Build a
     /// Build a
     /// [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)
     /// [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup)
     /// element.
     /// element.
-    optgroup;
+    optgroup {
+        disabled: Bool,
+        label: String,
+    };
+
     /// Build a
     /// Build a
     /// [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     /// [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
     /// element.
     /// element.
-    option;
+    option {
+        disabled: Bool,
+        label: String,
+        selected: Bool,
+        value: String,
+    };
+
     /// Build a
     /// Build a
     /// [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)
     /// [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output)
     /// element.
     /// element.
-    output;
+    output {
+        form: Id,
+        name: Id,
+        // r#for: SpacedSet<Id>,
+    };
+
     /// Build a
     /// Build a
     /// [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
     /// [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress)
     /// element.
     /// element.
-    progress;
+    progress {
+        max: f64,
+        value: f64,
+    };
+
     /// Build a
     /// Build a
     /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
     /// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
     /// element.
     /// element.
-    select;
+    select {
+        autocomplete: String,
+        autofocus: Bool,
+        disabled: Bool,
+        form: Id,
+        multiple: Bool,
+        name: Id,
+        required: Bool,
+        size: usize,
+    };
+
     /// Build a
     /// Build a
     /// [`<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
     /// [`<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)
     /// element.
     /// element.
-    textarea;
+    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
     // Interactive elements
 
 
     /// Build a
     /// Build a
     /// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
     /// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
     /// element.
     /// element.
-    details;
+    details {
+        open: Bool,
+    };
+
 
 
 
 
     /// Build a
     /// Build a
     /// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
     /// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
     /// element.
     /// element.
-    summary;
+    summary {};
 
 
     // Web components
     // Web components
 
 
     /// Build a
     /// Build a
     /// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
     /// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
     /// element.
     /// element.
-    slot;
+    slot {};
 
 
     /// Build a
     /// Build a
     /// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
     /// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
     /// element.
     /// element.
-    template;
+    template {};
 }
 }
 
 
 builder_constructors! {
 builder_constructors! {
@@ -583,36 +1218,210 @@ builder_constructors! {
     /// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
     /// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
     /// element.
     /// element.
     svg <> "http://www.w3.org/2000/svg" ;
     svg <> "http://www.w3.org/2000/svg" ;
+
     /// Build a
     /// Build a
     /// [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)
     /// [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)
     /// element.
     /// element.
     path <> "http://www.w3.org/2000/svg";
     path <> "http://www.w3.org/2000/svg";
+
     /// Build a
     /// Build a
     /// [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)
     /// [`<circle>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle)
     /// element.
     /// element.
     circle <>  "http://www.w3.org/2000/svg";
     circle <>  "http://www.w3.org/2000/svg";
+
     /// Build a
     /// Build a
     /// [`<ellipse>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse)
     /// [`<ellipse>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse)
     /// element.
     /// element.
     ellipse <> "http://www.w3.org/2000/svg";
     ellipse <> "http://www.w3.org/2000/svg";
+
     /// Build a
     /// Build a
     /// [`<line>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line)
     /// [`<line>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line)
     /// element.
     /// element.
     line <> "http://www.w3.org/2000/svg";
     line <> "http://www.w3.org/2000/svg";
+
     /// Build a
     /// Build a
     /// [`<polygon>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon)
     /// [`<polygon>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon)
     /// element.
     /// element.
     polygon <> "http://www.w3.org/2000/svg";
     polygon <> "http://www.w3.org/2000/svg";
+
     /// Build a
     /// Build a
     /// [`<polyline>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline)
     /// [`<polyline>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline)
     /// element.
     /// element.
     polyline <> "http://www.w3.org/2000/svg";
     polyline <> "http://www.w3.org/2000/svg";
+
     /// Build a
     /// Build a
     /// [`<rect>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect)
     /// [`<rect>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/rect)
     /// element.
     /// element.
     rect <> "http://www.w3.org/2000/svg";
     rect <> "http://www.w3.org/2000/svg";
+
     /// Build a
     /// Build a
     /// [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image)
     /// [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image)
     /// element.
     /// element.
     image <> "http://www.w3.org/2000/svg";
     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())
+    /// ```
+
+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
+*/
+
+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!()
+    }
 }
 }

+ 8 - 7
packages/ssr/src/lib.rs

@@ -8,7 +8,7 @@
 
 
 use std::fmt::{Display, Formatter};
 use std::fmt::{Display, Formatter};
 
 
-use dioxus_core::{nodes::VNode, virtual_dom::VirtualDom};
+use dioxus_core::*;
 
 
 pub fn render_root(vdom: &VirtualDom) -> String {
 pub fn render_root(vdom: &VirtualDom) -> String {
     format!("{:}", TextRenderer::new(vdom))
     format!("{:}", TextRenderer::new(vdom))
@@ -69,8 +69,8 @@ impl<'a> TextRenderer<'a> {
     }
     }
 
 
     fn html_render(&self, node: &VNode, f: &mut std::fmt::Formatter, il: u16) -> std::fmt::Result {
     fn html_render(&self, node: &VNode, f: &mut std::fmt::Formatter, il: u16) -> std::fmt::Result {
-        match node {
-            VNode::Text(text) => {
+        match &node.kind {
+            VNodeKind::Text(text) => {
                 if self.cfg.indent {
                 if self.cfg.indent {
                     for _ in 0..il {
                     for _ in 0..il {
                         write!(f, "    ")?;
                         write!(f, "    ")?;
@@ -78,7 +78,7 @@ impl<'a> TextRenderer<'a> {
                 }
                 }
                 write!(f, "{}", text.text)?
                 write!(f, "{}", text.text)?
             }
             }
-            VNode::Element(el) => {
+            VNodeKind::Element(el) => {
                 if self.cfg.indent {
                 if self.cfg.indent {
                     for _ in 0..il {
                     for _ in 0..il {
                         write!(f, "    ")?;
                         write!(f, "    ")?;
@@ -112,12 +112,12 @@ impl<'a> TextRenderer<'a> {
                     write!(f, "\n")?;
                     write!(f, "\n")?;
                 }
                 }
             }
             }
-            VNode::Fragment(frag) => {
+            VNodeKind::Fragment(frag) => {
                 for child in frag.children {
                 for child in frag.children {
                     self.html_render(child, f, il + 1)?;
                     self.html_render(child, f, il + 1)?;
                 }
                 }
             }
             }
-            VNode::Component(vcomp) => {
+            VNodeKind::Component(vcomp) => {
                 let idx = vcomp.ass_scope.get().unwrap();
                 let idx = vcomp.ass_scope.get().unwrap();
 
 
                 let new_node = self
                 let new_node = self
@@ -130,7 +130,7 @@ impl<'a> TextRenderer<'a> {
 
 
                 self.html_render(new_node, f, il + 1)?;
                 self.html_render(new_node, f, il + 1)?;
             }
             }
-            VNode::Suspended { .. } => todo!(),
+            VNodeKind::Suspended => todo!(),
         }
         }
         Ok(())
         Ok(())
     }
     }
@@ -151,6 +151,7 @@ mod tests {
     use dioxus_core as dioxus;
     use dioxus_core as dioxus;
     use dioxus_core::prelude::*;
     use dioxus_core::prelude::*;
     use dioxus_html as dioxus_elements;
     use dioxus_html as dioxus_elements;
+    use dioxus_html::GlobalAttributes;
 
 
     const SIMPLE_APP: FC<()> = |cx| {
     const SIMPLE_APP: FC<()> = |cx| {
         cx.render(rsx!(div {
         cx.render(rsx!(div {

+ 11 - 7
packages/web/Cargo.toml

@@ -8,10 +8,10 @@ license = "MIT/Apache-2.0"
 # 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-html = { path="../html" }
+dioxus-core = { path = "../core", version = "0.1.2" }
+dioxus-html = { path = "../html" }
 js-sys = "0.3"
 js-sys = "0.3"
-wasm-bindgen = { version="0.2.71", features=["enable-interning"] }
+wasm-bindgen = { version = "0.2.71", features = ["enable-interning"] }
 lazy_static = "1.4.0"
 lazy_static = "1.4.0"
 wasm-bindgen-futures = "0.4.20"
 wasm-bindgen-futures = "0.4.20"
 wasm-logger = "0.2.0"
 wasm-logger = "0.2.0"
@@ -22,7 +22,7 @@ console_error_panic_hook = "0.1.6"
 generational-arena = "0.2.8"
 generational-arena = "0.2.8"
 wasm-bindgen-test = "0.3.21"
 wasm-bindgen-test = "0.3.21"
 once_cell = "1.7.2"
 once_cell = "1.7.2"
-atoms = { path="../atoms" }
+atoms = { path = "../atoms" }
 async-channel = "1.6.1"
 async-channel = "1.6.1"
 anyhow = "1.0.41"
 anyhow = "1.0.41"
 slotmap = "1.0.3"
 slotmap = "1.0.3"
@@ -75,7 +75,11 @@ crate-type = ["cdylib", "rlib"]
 im-rc = "15.0.0"
 im-rc = "15.0.0"
 # rand = { version="0.8.4", features=["small_rng"] }
 # rand = { version="0.8.4", features=["small_rng"] }
 separator = "0.4.1"
 separator = "0.4.1"
-uuid = { version="0.8.2", features=["v4", "wasm-bindgen"] }
-dioxus-hooks = { path="../hooks" }
-gloo-timers = { version="0.2.1", features=["futures"] }
+uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
+dioxus-hooks = { path = "../hooks" }
+gloo-timers = { version = "0.2.1", features = ["futures"] }
+serde = { version = "1.0.126", features = ["derive"] }
+surf = { version = "2.2.0", default-features = false, features = [
+    "wasm-client",
+] }
 # wasm-timer = "0.2.5"
 # wasm-timer = "0.2.5"

+ 35 - 18
packages/web/examples/async_web.rs

@@ -17,34 +17,47 @@ use dioxus_web::*;
 
 
 fn main() {
 fn main() {
     // Setup logging
     // Setup logging
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+    // wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
     console_error_panic_hook::set_once();
     console_error_panic_hook::set_once();
 
 
     // Run the app
     // Run the app
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
 }
 }
 
 
+#[derive(serde::Deserialize)]
+struct DogApi {
+    message: String,
+}
+
+const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random/";
+
 static App: FC<()> = |cx| {
 static App: FC<()> = |cx| {
     // let mut count = use_state(cx, || 0);
     // let mut count = use_state(cx, || 0);
-    let fut = cx.use_hook(
-        move || {
-            Box::pin(async {
-                let mut tick: i32 = 0;
-                log::debug!("yeet!");
-                loop {
-                    gloo_timers::future::TimeoutFuture::new(250).await;
-                    log::debug!("ticking forward... {}", tick);
-                    tick += 1;
-                }
-            }) as Pin<Box<dyn Future<Output = ()> + 'static>>
-        },
-        |h| h,
-        |_| {},
-    );
+    let state = use_state(cx, || 0);
+    let set_val = state.setter();
 
 
-    cx.submit_task(fut);
+    let g = cx.use_task(|| async move {
+        let mut tick: i32 = 0;
+        log::debug!("yeet!");
+        // loop {
+        //     gloo_timers::future::TimeoutFuture::new(250).await;
+        //     log::debug!("ticking forward... {}", tick);
+        //     tick += 1;
+        //     if tick > 10 {
+        //         break;
+        //     }
+        // }
+
+        set_val(10);
+        surf::get(ENDPOINT).recv_json::<DogApi>().await
+        // String::from("Huzza!")
+    });
+
+    let dog_node = match g.as_ref().and_then(|f| f.as_ref().ok()) {
+        Some(res) => rsx!(in cx, img { src: "{res.message}" }),
+        None => rsx!(in cx, div { "No doggos for you :(" }),
+    };
 
 
-    let state = use_state(cx, || 0);
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {
             section { class: "py-12 px-4 text-center"
             section { class: "py-12 px-4 text-center"
@@ -64,6 +77,10 @@ static App: FC<()> = |cx| {
                             "decr"
                             "decr"
                         }
                         }
                     }
                     }
+                    div {
+                        h1{"doggo!"}
+                        {dog_node}
+                    }
                 }
                 }
             }
             }
         }
         }

+ 47 - 34
packages/web/src/lib.rs

@@ -62,20 +62,27 @@ impl WebsysRenderer {
     }
     }
 
 
     pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
     pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
-        let body_element = prepare_websys_dom();
+        use wasm_bindgen::JsCast;
 
 
-        let root_node = body_element.first_child().unwrap();
+        let root = prepare_websys_dom();
+        let root_node = root.clone().dyn_into::<Node>().unwrap();
 
 
-        let mut websys_dom = crate::new::WebsysDom::new(body_element.clone());
+        let mut websys_dom = crate::new::WebsysDom::new(root.clone());
 
 
+        websys_dom.stack.push(root_node.clone());
         websys_dom.stack.push(root_node);
         websys_dom.stack.push(root_node);
 
 
         self.internal_dom.rebuild(&mut websys_dom)?;
         self.internal_dom.rebuild(&mut websys_dom)?;
 
 
         log::info!("Going into event loop");
         log::info!("Going into event loop");
-        loop {
-            let trigger = {
-                let real_queue = websys_dom.wait_for_event();
+        // loop {
+        let trigger = {
+            let real_queue = websys_dom.wait_for_event();
+            if self.internal_dom.tasks.is_empty() {
+                log::info!("tasks is empty, waiting for dom event to trigger soemthing");
+                real_queue.await
+            } else {
+                log::info!("tasks is not empty, waiting for either tasks or event system");
                 let task_queue = (&mut self.internal_dom.tasks).next();
                 let task_queue = (&mut self.internal_dom.tasks).next();
 
 
                 pin_mut!(real_queue);
                 pin_mut!(real_queue);
@@ -85,26 +92,30 @@ impl WebsysRenderer {
                     futures_util::future::Either::Left((trigger, _)) => trigger,
                     futures_util::future::Either::Left((trigger, _)) => trigger,
                     futures_util::future::Either::Right((trigger, _)) => trigger,
                     futures_util::future::Either::Right((trigger, _)) => trigger,
                 }
                 }
-            };
+            }
+        };
 
 
+        if let Some(real_trigger) = trigger {
             log::info!("event received");
             log::info!("event received");
-            let root_node = body_element.first_child().unwrap();
-            websys_dom.stack.push(root_node.clone());
-            self.internal_dom
-                .progress_with_event(&mut websys_dom, trigger.unwrap())?;
+            // let root_node = body_element.first_child().unwrap();
+            // websys_dom.stack.push(root_node.clone());
 
 
-            // let t2 = self.internal_dom.tasks.next();
-            // futures::select! {
-            //     trigger = t1 => {
-            //         log::info!("event received");
-            //         let root_node = body_element.first_child().unwrap();
-            //         websys_dom.stack.push(root_node.clone());
-            //         self.internal_dom
-            //             .progress_with_event(&mut websys_dom, trigger)?;
-            //     },
-            //     () = t2 => {}
-            // };
+            self.internal_dom
+                .progress_with_event(&mut websys_dom, real_trigger)?;
         }
         }
+
+        // let t2 = self.internal_dom.tasks.next();
+        // futures::select! {
+        //     trigger = t1 => {
+        //         log::info!("event received");
+        //         let root_node = body_element.first_child().unwrap();
+        //         websys_dom.stack.push(root_node.clone());
+        //         self.internal_dom
+        //             .progress_with_event(&mut websys_dom, trigger)?;
+        //     },
+        //     () = t2 => {}
+        // };
+        // }
         // while let Some(trigger) = websys_dom.wait_for_event().await {
         // while let Some(trigger) = websys_dom.wait_for_event().await {
         // }
         // }
 
 
@@ -119,21 +130,23 @@ fn prepare_websys_dom() -> Element {
     let document = window
     let document = window
         .document()
         .document()
         .expect("should have access to the Document");
         .expect("should have access to the Document");
-    let body = document.body().unwrap();
+
+    // let body = document.body().unwrap();
+    let el = document.get_element_by_id("dioxusroot").unwrap();
 
 
     // Build a dummy div
     // Build a dummy div
-    let container: &Element = body.as_ref();
+    // let container: &Element = body.as_ref();
     // container.set_inner_html("");
     // container.set_inner_html("");
-    container
-        .append_child(
-            document
-                .create_element("div")
-                .expect("should create element OK")
-                .as_ref(),
-        )
-        .expect("should append child OK");
-
-    container.clone()
+    // container
+    //     .append_child(
+    //         document
+    //             .create_element("div")
+    //             .expect("should create element OK")
+    //             .as_ref(),
+    //     )
+    //     .expect("should append child OK");
+    el
+    // container.clone()
 }
 }
 
 
 // Progress the mount of the root component
 // Progress the mount of the root component

+ 9 - 4
packages/web/src/new.rs

@@ -52,6 +52,7 @@ impl WebsysDom {
         let mut nodes = slotmap::SlotMap::with_capacity(1000);
         let mut nodes = slotmap::SlotMap::with_capacity(1000);
 
 
         let root_id = nodes.insert(root.clone().dyn_into::<Node>().unwrap());
         let root_id = nodes.insert(root.clone().dyn_into::<Node>().unwrap());
+
         Self {
         Self {
             stack: Stack::with_capacity(10),
             stack: Stack::with_capacity(10),
             nodes,
             nodes,
@@ -72,9 +73,13 @@ impl WebsysDom {
 
 
 impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
 impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
     fn push(&mut self, root: RealDomNode) {
     fn push(&mut self, root: RealDomNode) {
-        log::debug!("Called [push_root] {:?}", root);
+        log::debug!("Called [push_root] {:#?}", root);
         let key: DefaultKey = KeyData::from_ffi(root.0).into();
         let key: DefaultKey = KeyData::from_ffi(root.0).into();
-        let domnode = self.nodes.get(key).expect("Failed to pop know root");
+        let domnode = self
+            .nodes
+            .get(key)
+            .expect(&format!("Failed to pop know root: {:#?}", key));
+
         self.stack.push(domnode.clone());
         self.stack.push(domnode.clone());
     }
     }
     // drop the node off the stack
     // drop the node off the stack
@@ -85,10 +90,10 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
     fn append_children(&mut self, many: u32) {
     fn append_children(&mut self, many: u32) {
         log::debug!("Called [`append_child`]");
         log::debug!("Called [`append_child`]");
 
 
-        let mut root: Node = self
+        let root: Node = self
             .stack
             .stack
             .list
             .list
-            .get(self.stack.list.len() - many as usize)
+            .get(self.stack.list.len() - (1 + many as usize))
             .unwrap()
             .unwrap()
             .clone();
             .clone();
 
 

+ 4 - 2
packages/webview/examples/test.rs

@@ -1,3 +1,6 @@
+use dioxus_core as dioxus;
+use dioxus_core::prelude::*;
+
 fn main() {
 fn main() {
     dioxus_webview::launch(App, |f| f.with_focus().with_maximized(true)).expect("Failed");
     dioxus_webview::launch(App, |f| f.with_focus().with_maximized(true)).expect("Failed");
 }
 }
@@ -11,8 +14,6 @@ static App: FC<()> = |cx| {
     ))
     ))
 };
 };
 
 
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
 mod dioxus_elements {
 mod dioxus_elements {
     use super::*;
     use super::*;
     pub struct div;
     pub struct div;
@@ -20,4 +21,5 @@ mod dioxus_elements {
         const TAG_NAME: &'static str = "div";
         const TAG_NAME: &'static str = "div";
         const NAME_SPACE: Option<&'static str> = None;
         const NAME_SPACE: Option<&'static str> = None;
     }
     }
+    pub trait GlobalAttributes {}
 }
 }

+ 9 - 150
packages/webview/src/lib.rs

@@ -3,8 +3,12 @@ use std::ops::{Deref, DerefMut};
 use std::sync::mpsc::channel;
 use std::sync::mpsc::channel;
 use std::sync::{Arc, RwLock};
 use std::sync::{Arc, RwLock};
 
 
-use dioxus_core::virtual_dom::VirtualDom;
-use dioxus_core::{prelude::*, serialize::DomEdit};
+use dioxus_core::*;
+
+use wry::application::event::{Event, WindowEvent};
+use wry::application::event_loop::{ControlFlow, EventLoop};
+use wry::application::window::Fullscreen;
+use wry::webview::WebViewBuilder;
 use wry::{
 use wry::{
     application::window::{Window, WindowBuilder},
     application::window::{Window, WindowBuilder},
     webview::{RpcRequest, RpcResponse},
     webview::{RpcRequest, RpcResponse},
@@ -57,15 +61,6 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
         user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
         user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
         redits: Option<Vec<DomEdit<'static>>>,
         redits: Option<Vec<DomEdit<'static>>>,
     ) -> anyhow::Result<()> {
     ) -> anyhow::Result<()> {
-        use wry::{
-            application::{
-                event::{Event, StartCause, WindowEvent},
-                event_loop::{ControlFlow, EventLoop},
-                window::WindowBuilder,
-            },
-            webview::WebViewBuilder,
-        };
-
         let event_loop = EventLoop::new();
         let event_loop = EventLoop::new();
 
 
         let window = user_builder(WindowBuilder::new()).build(&event_loop)?;
         let window = user_builder(WindowBuilder::new()).build(&event_loop)?;
@@ -78,7 +73,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
 
 
         let webview = WebViewBuilder::new(window)?
         let webview = WebViewBuilder::new(window)?
             .with_url(&format!("data:text/html,{}", HTML_CONTENT))?
             .with_url(&format!("data:text/html,{}", HTML_CONTENT))?
-            .with_rpc_handler(move |window: &Window, mut req: RpcRequest| {
+            .with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
                 match req.method.as_str() {
                 match req.method.as_str() {
                     "initiate" => {
                     "initiate" => {
                         let edits = if let Some(edits) = &redits {
                         let edits = if let Some(edits) = &redits {
@@ -144,45 +139,6 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
                 }
                 }
             }
             }
         });
         });
-
-        // let mut view = web_view::builder()
-        //     .invoke_handler(|view, arg| {
-        //         let handle = view.handle();
-        //         sender
-        //             .send(InnerEvent::Initiate(handle))
-        //             .expect("should not fail");
-
-        //         Ok(())
-        //     })
-        //     .content(web_view::Content::Html(HTML_CONTENT))
-        //     .user_data(())
-        //     .title(title)
-        //     .size(width, height)
-        //     .resizable(resizable)
-        //     .debug(debug)
-        //     .frameless(frameless)
-        //     .visible(visible)
-        //     .min_size(min_width, min_height)
-        //     .build()
-        //     .unwrap();
-        // loop {
-        //     view.step()
-        //         .expect("should not fail")
-        //         .expect("should not fail");
-        //     std::thread::sleep(std::time::Duration::from_millis(15));
-
-        //     if let Ok(event) = receiver.try_recv() {
-        //         if let InnerEvent::Initiate(handle) = event {
-        //             let editlist = ref_edits.clone();
-        //             handle
-        //                 .dispatch(move |view| {
-        //                     let escaped = escape(&editlist);
-        //                     view.eval(&format!("EditListReceived({});", escaped))
-        //                 })
-        //                 .expect("Dispatch failed");
-        //         }
-        //     }
-        // }
     }
     }
 
 
     /// Create a new text-renderer instance from a functional component root.
     /// Create a new text-renderer instance from a functional component root.
@@ -200,12 +156,12 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
     }
     }
 
 
     /// Pass new args to the root function
     /// Pass new args to the root function
-    pub fn update(&mut self, new_val: T) {
+    pub fn update(&mut self, _new_val: T) {
         todo!()
         todo!()
     }
     }
 
 
     /// Modify the root function in place, forcing a re-render regardless if the props changed
     /// Modify the root function in place, forcing a re-render regardless if the props changed
-    pub fn update_mut(&mut self, modifier: impl Fn(&mut T)) {
+    pub fn update_mut(&mut self, _modifier: impl Fn(&mut T)) {
         todo!()
         todo!()
     }
     }
 }
 }
@@ -221,15 +177,6 @@ struct MessageParameters {
 }
 }
 
 
 fn HANDLER(window: &Window, mut req: RpcRequest) -> Option<RpcResponse> {
 fn HANDLER(window: &Window, mut req: RpcRequest) -> Option<RpcResponse> {
-    use wry::{
-        application::{
-            event::{Event, WindowEvent},
-            event_loop::{ControlFlow, EventLoop},
-            window::{Fullscreen, Window, WindowBuilder},
-        },
-        webview::{RpcRequest, RpcResponse, WebViewBuilder},
-    };
-
     let mut response = None;
     let mut response = None;
     if &req.method == "fullscreen" {
     if &req.method == "fullscreen" {
         if let Some(params) = req.params.take() {
         if let Some(params) = req.params.take() {
@@ -262,91 +209,3 @@ fn HANDLER(window: &Window, mut req: RpcRequest) -> Option<RpcResponse> {
 
 
     response
     response
 }
 }
-
-pub struct DioxusWebviewBuilder<'a> {
-    pub(crate) title: &'a str,
-    pub(crate) width: i32,
-    pub(crate) height: i32,
-    pub(crate) resizable: bool,
-    pub(crate) debug: bool,
-    pub(crate) frameless: bool,
-    pub(crate) visible: bool,
-    pub(crate) min_width: i32,
-    pub(crate) min_height: i32,
-}
-impl<'a> DioxusWebviewBuilder<'a> {
-    fn new() -> Self {
-        #[cfg(debug_assertions)]
-        let debug = true;
-        #[cfg(not(debug_assertions))]
-        let debug = false;
-
-        DioxusWebviewBuilder {
-            title: "Application",
-            width: 800,
-            height: 600,
-            resizable: true,
-            debug,
-            frameless: false,
-            visible: true,
-            min_width: 300,
-            min_height: 300,
-        }
-    }
-    /// Sets the title of the WebView window.
-    ///
-    /// Defaults to `"Application"`.
-    pub fn title(mut self, title: &'a str) -> Self {
-        self.title = title;
-        self
-    }
-
-    /// Sets the size of the WebView window.
-    ///
-    /// Defaults to 800 x 600.
-    pub fn size(mut self, width: i32, height: i32) -> Self {
-        self.width = width;
-        self.height = height;
-        self
-    }
-
-    /// Sets the resizability of the WebView window. If set to false, the window cannot be resized.
-    ///
-    /// Defaults to `true`.
-    pub fn resizable(mut self, resizable: bool) -> Self {
-        self.resizable = resizable;
-        self
-    }
-
-    /// Enables or disables debug mode.
-    ///
-    /// Defaults to `true` for debug builds, `false` for release builds.
-    pub fn debug(mut self, debug: bool) -> Self {
-        self.debug = debug;
-        self
-    }
-    /// The window crated will be frameless
-    ///
-    /// defaults to `false`
-    pub fn frameless(mut self, frameless: bool) -> Self {
-        self.frameless = frameless;
-        self
-    }
-
-    /// Set the visibility of the WebView window.
-    ///
-    /// defaults to `true`
-    pub fn visible(mut self, visible: bool) -> Self {
-        self.visible = visible;
-        self
-    }
-
-    /// Sets the minimum size of the WebView window.
-    ///
-    /// Defaults to 300 x 300.
-    pub fn min_size(mut self, width: i32, height: i32) -> Self {
-        self.min_width = width;
-        self.min_height = height;
-        self
-    }
-}

+ 1 - 0
src/lib.rs

@@ -188,6 +188,7 @@ pub use dioxus_webview as desktop;
 pub mod prelude {
 pub mod prelude {
     //! A glob import that includes helper types like FC, rsx!, html!, and required traits
     //! A glob import that includes helper types like FC, rsx!, html!, and required traits
     pub use dioxus_core::prelude::*;
     pub use dioxus_core::prelude::*;
+    pub use dioxus_elements::GlobalAttributes;
     pub use dioxus_hooks::*;
     pub use dioxus_hooks::*;
     pub use dioxus_html as dioxus_elements;
     pub use dioxus_html as dioxus_elements;
 }
 }