Преглед изворни кода

update the web renderer to the new dioxus core

Evan Almloff пре 1 година
родитељ
комит
88e2da6c11

+ 1 - 1
examples/compose.rs

@@ -61,7 +61,7 @@ fn compose(cx: Scope<ComposeProps>) -> Element {
                 },
                 "Click to send"
             }
-          
+
             input { oninput: move |e| user_input.set(e.value()), value: "{user_input}" }
         }
     })

+ 1 - 1
packages/core/src/mutations.rs

@@ -128,7 +128,7 @@ pub trait WriteMutations {
     /// Push the given root node onto our stack.
     ///
     /// Id: The ID of the root node to push.
-    fn push_root(&mut self, _id: ElementId);
+    fn push_root(&mut self, id: ElementId);
 
     /// Swap to a new subtree
     fn swap_subtree(&mut self, _subtree_index: usize) {}

+ 18 - 3
packages/core/tests/event_propagation.rs

@@ -12,7 +12,12 @@ fn events_propagate() {
     _ = dom.rebuild();
 
     // Top-level click is registered
-    dom.handle_event("click", Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())), ElementId(1), true);
+    dom.handle_event(
+        "click",
+        Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())),
+        ElementId(1),
+        true,
+    );
     assert_eq!(*CLICKS.lock().unwrap(), 1);
 
     // break reference....
@@ -22,7 +27,12 @@ fn events_propagate() {
     }
 
     // Lower click is registered
-    dom.handle_event("click", Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())), ElementId(2), true);
+    dom.handle_event(
+        "click",
+        Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())),
+        ElementId(2),
+        true,
+    );
     assert_eq!(*CLICKS.lock().unwrap(), 3);
 
     // break reference....
@@ -32,7 +42,12 @@ fn events_propagate() {
     }
 
     // Stop propagation occurs
-    dom.handle_event("click", Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())), ElementId(2), true);
+    dom.handle_event(
+        "click",
+        Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())),
+        ElementId(2),
+        true,
+    );
     assert_eq!(*CLICKS.lock().unwrap(), 3);
 }
 

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

@@ -149,7 +149,7 @@ pub fn launch_with_props<P: Clone + 'static>(root: Component<P>, props: P, cfg:
 
     // Copy over any assets we find
     crate::collect_assets::copy_assets();
-  
+
     // Set the event converter
     dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter));
 

+ 1 - 1
packages/html/src/events.rs

@@ -16,7 +16,7 @@ macro_rules! impl_event {
                 ::dioxus_core::Attribute::new(
                     stringify!($name),
 ::dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<crate::PlatformEventData>| {
-                        _f(e.map(|e|e.into())).spawn(_cx);
+                        _f(e.map(|e|e.into())).spawn();
                     }),
                     None,
                     false,

+ 5 - 166
packages/web/src/dom.rs

@@ -6,9 +6,7 @@
 //! - tests to ensure dyn_into works for various event types.
 //! - Partial delegation?
 
-use dioxus_core::{
-    BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
-};
+use dioxus_core::{ElementId, Mutation, Template, TemplateAttribute, TemplateNode};
 use dioxus_html::{event_bubbles, MountedData, PlatformEventData};
 use dioxus_interpreter_js::get_node;
 use dioxus_interpreter_js::{minimal_bindings, save_template, Channel};
@@ -20,14 +18,14 @@ use web_sys::{Document, Element, Event};
 use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
 
 pub struct WebsysDom {
-    document: Document,
+    pub(crate) document: Document,
     #[allow(dead_code)]
     pub(crate) root: Element,
-    templates: FxHashMap<String, u16>,
-    max_template_id: u16,
+    pub(crate) templates: FxHashMap<String, u16>,
+    pub(crate) max_template_id: u16,
     pub(crate) interpreter: Channel,
     #[cfg(feature = "mounted")]
-    event_channel: mpsc::UnboundedSender<UiEvent>,
+    pub(crate) event_channel: mpsc::UnboundedSender<UiEvent>,
 }
 
 pub struct UiEvent {
@@ -118,165 +116,6 @@ impl WebsysDom {
     pub fn mount(&mut self) {
         self.interpreter.mount_to_root();
     }
-
-    pub fn load_templates(&mut self, templates: &[Template]) {
-        for template in templates {
-            let mut roots = vec![];
-
-            for root in template.roots {
-                roots.push(self.create_template_node(root))
-            }
-
-            self.templates
-                .insert(template.name.to_owned(), self.max_template_id);
-            save_template(roots, self.max_template_id);
-            self.max_template_id += 1
-        }
-    }
-
-    fn create_template_node(&self, v: &TemplateNode) -> web_sys::Node {
-        use TemplateNode::*;
-        match v {
-            Element {
-                tag,
-                namespace,
-                attrs,
-                children,
-                ..
-            } => {
-                let el = match namespace {
-                    Some(ns) => self.document.create_element_ns(Some(ns), tag).unwrap(),
-                    None => self.document.create_element(tag).unwrap(),
-                };
-                for attr in *attrs {
-                    if let TemplateAttribute::Static {
-                        name,
-                        value,
-                        namespace,
-                    } = attr
-                    {
-                        minimal_bindings::setAttributeInner(
-                            el.clone().into(),
-                            name,
-                            JsValue::from_str(value),
-                            *namespace,
-                        );
-                    }
-                }
-                for child in *children {
-                    let _ = el.append_child(&self.create_template_node(child));
-                }
-                el.dyn_into().unwrap()
-            }
-            Text { text } => self.document.create_text_node(text).dyn_into().unwrap(),
-            DynamicText { .. } => self.document.create_text_node("p").dyn_into().unwrap(),
-            Dynamic { .. } => {
-                let el = self.document.create_element("pre").unwrap();
-                let _ = el.toggle_attribute("hidden");
-                el.dyn_into().unwrap()
-            }
-        }
-    }
-
-    pub fn apply_edits(&mut self, mut edits: Vec<Mutation>) {
-        use Mutation::*;
-        let i = &mut self.interpreter;
-        #[cfg(feature = "mounted")]
-        // we need to apply the mount events last, so we collect them here
-        let mut to_mount = Vec::new();
-        for edit in &edits {
-            match edit {
-                AppendChildren { id, m } => i.append_children(id.0 as u32, *m as u16),
-                AssignId { path, id } => {
-                    i.assign_id(path.as_ptr() as u32, path.len() as u8, id.0 as u32)
-                }
-                CreatePlaceholder { id } => i.create_placeholder(id.0 as u32),
-                CreateTextNode { value, id } => i.create_text_node(value, id.0 as u32),
-                HydrateText { path, value, id } => {
-                    i.hydrate_text(path.as_ptr() as u32, path.len() as u8, value, id.0 as u32)
-                }
-                LoadTemplate { name, index, id } => {
-                    if let Some(tmpl_id) = self.templates.get(*name) {
-                        i.load_template(*tmpl_id, *index as u16, id.0 as u32)
-                    }
-                }
-                ReplaceWith { id, m } => i.replace_with(id.0 as u32, *m as u16),
-                ReplacePlaceholder { path, m } => {
-                    i.replace_placeholder(path.as_ptr() as u32, path.len() as u8, *m as u16)
-                }
-                InsertAfter { id, m } => i.insert_after(id.0 as u32, *m as u16),
-                InsertBefore { id, m } => i.insert_before(id.0 as u32, *m as u16),
-                SetAttribute {
-                    name,
-                    value,
-                    id,
-                    ns,
-                } => match value {
-                    BorrowedAttributeValue::Text(txt) => {
-                        i.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
-                    }
-                    BorrowedAttributeValue::Float(f) => {
-                        i.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
-                    }
-                    BorrowedAttributeValue::Int(n) => {
-                        i.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
-                    }
-                    BorrowedAttributeValue::Bool(b) => i.set_attribute(
-                        id.0 as u32,
-                        name,
-                        if *b { "true" } else { "false" },
-                        ns.unwrap_or_default(),
-                    ),
-                    BorrowedAttributeValue::None => {
-                        i.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
-                    }
-                    _ => unreachable!(),
-                },
-                SetText { value, id } => i.set_text(id.0 as u32, value),
-                NewEventListener { name, id, .. } => {
-                    match *name {
-                        // mounted events are fired immediately after the element is mounted.
-                        "mounted" => {
-                            #[cfg(feature = "mounted")]
-                            to_mount.push(*id);
-                        }
-                        _ => {
-                            i.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
-                        }
-                    }
-                }
-                RemoveEventListener { name, id } => match *name {
-                    "mounted" => {}
-                    _ => {
-                        i.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
-                    }
-                },
-                Remove { id } => i.remove(id.0 as u32),
-                PushRoot { id } => i.push_root(id.0 as u32),
-            }
-        }
-        edits.clear();
-        i.flush();
-
-        #[cfg(feature = "mounted")]
-        for id in to_mount {
-            self.send_mount_event(id);
-        }
-    }
-
-    pub(crate) fn send_mount_event(&self, id: ElementId) {
-        let node = get_node(id.0 as u32);
-        if let Some(element) = node.dyn_ref::<Element>() {
-            let data: MountedData = element.into();
-            let data = Box::new(data);
-            let _ = self.event_channel.unbounded_send(UiEvent {
-                name: "mounted".to_string(),
-                bubbles: false,
-                element: id,
-                data: PlatformEventData::new(data),
-            });
-        }
-    }
 }
 
 fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> {

+ 6 - 5
packages/web/src/eval.rs

@@ -1,5 +1,6 @@
 use async_trait::async_trait;
-use dioxus_core::ScopeState;
+use dioxus_core::prelude::provide_context;
+use dioxus_core::ScopeId;
 use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator};
 use js_sys::Function;
 use serde_json::Value;
@@ -7,12 +8,12 @@ use std::{cell::RefCell, rc::Rc, str::FromStr};
 use wasm_bindgen::prelude::*;
 
 /// Provides the WebEvalProvider through [`cx.provide_context`].
-pub fn init_eval(cx: &ScopeState) {
+pub fn init_eval() {
     let provider: Rc<dyn EvalProvider> = Rc::new(WebEvalProvider {});
-    cx.provide_context(provider);
+    ScopeId::ROOT.provide_context(provider);
 }
 
-/// Reprents the web-target's provider of evaluators.
+/// Represents the web-target's provider of evaluators.
 pub struct WebEvalProvider;
 impl EvalProvider for WebEvalProvider {
     fn new_evaluator(&self, js: String) -> Result<Rc<dyn Evaluator>, EvalError> {
@@ -28,7 +29,7 @@ const PROMISE_WRAPPER: &str = r#"
     });
     "#;
 
-/// Reprents a web-target's JavaScript evaluator.
+/// Represents a web-target's JavaScript evaluator.
 pub struct WebEvaluator {
     dioxus: Dioxus,
     channel_receiver: async_channel::Receiver<serde_json::Value>,

+ 2 - 2
packages/web/src/hot_reload.rs

@@ -4,7 +4,7 @@ use futures_channel::mpsc::UnboundedReceiver;
 
 use dioxus_core::Template;
 
-pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
+pub(crate) fn init() -> UnboundedReceiver<Template> {
     use wasm_bindgen::closure::Closure;
     use wasm_bindgen::JsCast;
     use web_sys::{MessageEvent, WebSocket};
@@ -34,7 +34,7 @@ pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
             let val = serde_json::from_str::<serde_json::Value>(&string).unwrap();
             // leak the value
             let val: &'static serde_json::Value = Box::leak(Box::new(val));
-            let template: Template<'_> = Template::deserialize(val).unwrap();
+            let template: Template = Template::deserialize(val).unwrap();
             tx.unbounded_send(template).unwrap();
         }
     }) as Box<dyn FnMut(MessageEvent)>);

+ 24 - 24
packages/web/src/lib.rs

@@ -60,7 +60,7 @@ use std::rc::Rc;
 pub use crate::cfg::Config;
 #[cfg(feature = "file_engine")]
 pub use crate::file_engine::WebFileEngineExt;
-use dioxus_core::{Element, Scope, VirtualDom};
+use dioxus_core::{Element, VirtualDom};
 use futures_util::{
     future::{select, Either},
     pin_mut, FutureExt, StreamExt,
@@ -72,6 +72,7 @@ mod dom;
 #[cfg(feature = "eval")]
 mod eval;
 mod event;
+mod mutations;
 pub use event::*;
 #[cfg(feature = "file_engine")]
 mod file_engine;
@@ -105,8 +106,8 @@ mod rehydrate;
 ///     render!(div {"hello world"})
 /// }
 /// ```
-pub fn launch(root_component: fn(Scope) -> Element) {
-    launch_with_props(root_component, (), Config::default());
+pub fn launch(root_component: fn(()) -> Element) {
+    launch_with_props( root_component, (), Config::default());
 }
 
 /// Launch your app and run the event loop, with configuration.
@@ -122,13 +123,13 @@ pub fn launch(root_component: fn(Scope) -> Element) {
 ///     dioxus_web::launch_with_props(App, Config::new().pre_render(true));
 /// }
 ///
-/// fn app(cx: Scope) -> Element {
+/// fn app() -> Element {
 ///     cx.render(rsx!{
 ///         h1 {"hello world!"}
 ///     })
 /// }
 /// ```
-pub fn launch_cfg(root: fn(Scope) -> Element, config: Config) {
+pub fn launch_cfg(root: fn(()) -> Element, config: Config) {
     launch_with_props(root, (), config)
 }
 
@@ -156,8 +157,8 @@ pub fn launch_cfg(root: fn(Scope) -> Element, config: Config) {
 ///     render!(div {"hello {cx.props.name}"})
 /// }
 /// ```
-pub fn launch_with_props<T: 'static>(
-    root_component: fn(Scope<T>) -> Element,
+pub fn launch_with_props<T: Clone + 'static>(
+    root_component: fn(T) -> Element,
     root_properties: T,
     config: Config,
 ) {
@@ -176,7 +177,11 @@ pub fn launch_with_props<T: 'static>(
 ///     wasm_bindgen_futures::spawn_local(app_fut);
 /// }
 /// ```
-pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_props: T, cfg: Config) {
+pub async fn run_with_props<T: Clone + 'static>(
+    root: fn(T) -> Element,
+    root_props: T,
+    cfg: Config,
+) {
     tracing::info!("Starting up");
 
     let mut dom = VirtualDom::new_with_props(root, root_props);
@@ -184,8 +189,9 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
     #[cfg(feature = "eval")]
     {
         // Eval
-        let cx = dom.base_scope();
-        eval::init_eval(cx);
+        dom.in_runtime(|| {
+            eval::init_eval();
+        });
     }
 
     #[cfg(feature = "panic_hook")]
@@ -221,28 +227,23 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
             // it's a waste to produce edits just to get the vdom loaded
 
             {
-                let mutations = dom.rebuild();
-                web_sys::console::log_1(&format!("mutations: {:#?}", mutations).into());
-                let templates = mutations.templates;
-                websys_dom.load_templates(&templates);
-                websys_dom.interpreter.flush();
+                dom.rebuild(&mut websys_dom);
+                websys_dom.flush_edits();
             }
             if let Err(err) = websys_dom.rehydrate(&dom) {
                 tracing::error!("Rehydration failed. {:?}", err);
                 tracing::error!("Rebuild DOM into element from scratch");
                 websys_dom.root.set_text_content(None);
 
-                let edits = dom.rebuild();
+                dom.rebuild(&mut websys_dom);
 
-                websys_dom.load_templates(&edits.templates);
-                websys_dom.apply_edits(edits.edits);
+                websys_dom.flush_edits();
             }
         }
     } else {
-        let edits = dom.rebuild();
+        dom.rebuild(&mut websys_dom);
 
-        websys_dom.load_templates(&edits.templates);
-        websys_dom.apply_edits(edits.edits);
+        websys_dom.flush_edits();
     }
 
     // the mutations come back with nothing - we need to actually mount them
@@ -298,12 +299,11 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
         // let deadline = work_loop.wait_for_idle_time().await;
 
         // run the virtualdom work phase until the frame deadline is reached
-        let edits = dom.render_immediate();
+        dom.render_immediate(&mut websys_dom);
 
         // wait for the animation frame to fire so we can apply our changes
         // work_loop.wait_for_raf().await;
 
-        websys_dom.load_templates(&edits.templates);
-        websys_dom.apply_edits(edits.edits);
+        websys_dom.flush_edits();
     }
 }

+ 215 - 0
packages/web/src/mutations.rs

@@ -0,0 +1,215 @@
+use wasm_bindgen::JsCast;use crate::dom::UiEvent;
+use crate::dom::WebsysDom;
+use dioxus_core::prelude::*;
+use dioxus_core::WriteMutations;
+use dioxus_core::{AttributeValue, ElementId, ScopeId};
+use dioxus_html::event_bubbles;
+use dioxus_html::MountedData;
+use dioxus_html::PlatformEventData;
+use dioxus_interpreter_js::get_node;
+use dioxus_interpreter_js::minimal_bindings;
+use dioxus_interpreter_js::save_template;
+use wasm_bindgen::JsValue;
+
+impl WebsysDom {
+    fn create_template_node(&self, v: &TemplateNode) -> web_sys::Node {
+        use TemplateNode::*;
+        match v {
+            Element {
+                tag,
+                namespace,
+                attrs,
+                children,
+                ..
+            } => {
+                let el = match namespace {
+                    Some(ns) => self.document.create_element_ns(Some(ns), tag).unwrap(),
+                    None => self.document.create_element(tag).unwrap(),
+                };
+                for attr in *attrs {
+                    if let TemplateAttribute::Static {
+                        name,
+                        value,
+                        namespace,
+                    } = attr
+                    {
+                        minimal_bindings::setAttributeInner(
+                            el.clone().into(),
+                            name,
+                            JsValue::from_str(value),
+                            *namespace,
+                        );
+                    }
+                }
+                for child in *children {
+                    let _ = el.append_child(&self.create_template_node(child));
+                }
+                el.dyn_into().unwrap()
+            }
+            Text { text } => self.document.create_text_node(text).dyn_into().unwrap(),
+            DynamicText { .. } => self.document.create_text_node("p").dyn_into().unwrap(),
+            Dynamic { .. } => {
+                let el = self.document.create_element("pre").unwrap();
+                let _ = el.toggle_attribute("hidden");
+                el.dyn_into().unwrap()
+            }
+        }
+    }
+
+    pub fn flush_edits(&mut self) {
+        self.interpreter.flush()
+    }
+
+    #[cfg(feature = "mounted")]
+    pub(crate) fn send_mount_event(&self, id: ElementId) {
+        let node = get_node(id.0 as u32);
+        if let Some(element) = node.dyn_ref::<web_sys::Element>() {
+            let data: MountedData = element.into();
+            let data = Box::new(data);
+            let _ = self.event_channel.unbounded_send(UiEvent {
+                name: "mounted".to_string(),
+                bubbles: false,
+                element: id,
+                data: PlatformEventData::new(data),
+            });
+        }
+    }
+}
+
+impl WriteMutations for WebsysDom {
+    fn register_template(&mut self, template: Template) {
+        let mut roots = vec![];
+
+        for root in template.roots {
+            roots.push(self.create_template_node(root))
+        }
+
+        self.templates
+            .insert(template.name.to_owned(), self.max_template_id);
+        save_template(roots, self.max_template_id);
+        self.max_template_id += 1
+    }
+
+    fn append_children(&mut self, id: ElementId, m: usize) {
+        self.interpreter.append_children(id.0 as u32, m as u16)
+    }
+
+    fn assign_node_id(&mut self, path: &'static [u8], id: ElementId) {
+        self.interpreter
+            .assign_id(path.as_ptr() as u32, path.len() as u8, id.0 as u32)
+    }
+
+    fn create_placeholder(&mut self, id: ElementId) {
+        self.interpreter.create_placeholder(id.0 as u32)
+    }
+
+    fn create_text_node(&mut self, value: &str, id: ElementId) {
+        self.interpreter.create_text_node(value, id.0 as u32)
+    }
+
+    fn hydrate_text_node(&mut self, path: &'static [u8], value: &str, id: ElementId) {
+        self.interpreter
+            .hydrate_text(path.as_ptr() as u32, path.len() as u8, value, id.0 as u32)
+    }
+
+    fn load_template(&mut self, name: &'static str, index: usize, id: ElementId) {
+        if let Some(tmpl_id) = self.templates.get(name) {
+            self.interpreter
+                .load_template(*tmpl_id, index as u16, id.0 as u32)
+        }
+    }
+
+    fn replace_node_with(&mut self, id: ElementId, m: usize) {
+        self.interpreter.replace_with(id.0 as u32, m as u16)
+    }
+
+    fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize) {
+        self.interpreter
+            .replace_placeholder(path.as_ptr() as u32, path.len() as u8, m as u16)
+    }
+
+    fn insert_nodes_after(&mut self, id: ElementId, m: usize) {
+        self.interpreter.insert_after(id.0 as u32, m as u16)
+    }
+
+    fn insert_nodes_before(&mut self, id: ElementId, m: usize) {
+        self.interpreter.insert_before(id.0 as u32, m as u16)
+    }
+
+    fn set_attribute(
+        &mut self,
+        name: &'static str,
+        ns: Option<&'static str>,
+        value: &AttributeValue,
+        id: ElementId,
+    ) {
+        match value {
+            AttributeValue::Text(txt) => {
+                self.interpreter
+                    .set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
+            }
+            AttributeValue::Float(f) => self.interpreter.set_attribute(
+                id.0 as u32,
+                name,
+                &f.to_string(),
+                ns.unwrap_or_default(),
+            ),
+            AttributeValue::Int(n) => self.interpreter.set_attribute(
+                id.0 as u32,
+                name,
+                &n.to_string(),
+                ns.unwrap_or_default(),
+            ),
+            AttributeValue::Bool(b) => self.interpreter.set_attribute(
+                id.0 as u32,
+                name,
+                if *b { "true" } else { "false" },
+                ns.unwrap_or_default(),
+            ),
+            AttributeValue::None => {
+                self.interpreter
+                    .remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    fn set_node_text(&mut self, value: &str, id: ElementId) {
+        self.interpreter.set_text(id.0 as u32, value)
+    }
+
+    fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
+        match name {
+            // mounted events are fired immediately after the element is mounted.
+            "mounted" => {
+                #[cfg(feature = "mounted")]
+                self.send_mount_event(id);
+            }
+            _ => {
+                self.interpreter
+                    .new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
+            }
+        }
+    }
+
+    fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
+        match name {
+            "mounted" => {}
+            _ => {
+                self.interpreter.remove_event_listener(
+                    name,
+                    id.0 as u32,
+                    event_bubbles(name) as u8,
+                );
+            }
+        }
+    }
+
+    fn remove_node(&mut self, id: ElementId) {
+        self.interpreter.remove(id.0 as u32)
+    }
+
+    fn push_root(&mut self, id: ElementId) {
+        self.interpreter.push_root(id.0 as u32)
+    }
+}