瀏覽代碼

fix liveview mounted event

Evan Almloff 1 年之前
父節點
當前提交
c9612a085e

+ 2 - 2
packages/desktop/Cargo.toml

@@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react"]
 [dependencies]
 dioxus-core = { workspace = true, features = ["serialize"] }
 dioxus-html = { workspace = true, features = ["serialize", "native-bind"] }
-dioxus-interpreter-js = { workspace = true, features = ["sledgehammer"]}
+dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] }
 dioxus-hot-reload = { workspace = true, optional = true }
 
 serde = "1.0.136"
@@ -74,7 +74,7 @@ exitcode = "1.1.2"
 scraper = "0.16.0"
 
 [build-dependencies]
-dioxus-interpreter-js = { workspace = true, features = ["sledgehammer"] }
+dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] }
 minify-js = "0.5.6"
 
 # These tests need to be run on the main thread, so they cannot use rust's test harness.

+ 1 - 1
packages/desktop/build.rs

@@ -1,4 +1,4 @@
-use dioxus_interpreter_js::SLEDGEHAMMER_JS;
+use dioxus_interpreter_js::binary_protocol::SLEDGEHAMMER_JS;
 
 use std::io::Write;
 

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

@@ -8,7 +8,7 @@ use dioxus_core::ScopeState;
 use dioxus_core::VirtualDom;
 #[cfg(all(feature = "hot-reload", debug_assertions))]
 use dioxus_hot_reload::HotReloadMsg;
-use dioxus_interpreter_js::Channel;
+use dioxus_interpreter_js::binary_protocol::Channel;
 use rustc_hash::FxHashMap;
 use slab::Slab;
 use std::cell::RefCell;

+ 2 - 1
packages/interpreter/Cargo.toml

@@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 wasm-bindgen = { workspace = true, optional = true }
 js-sys = { version = "0.3.56", optional = true }
 web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] }
-sledgehammer_bindgen = { path = "/Users/evanalmloff/Desktop/Github/sledgehammer_bindgen", default-features = false, optional = true }
+sledgehammer_bindgen = { git = "https://github.com/ealmloff/sledgehammer_bindgen", default-features = false, optional = true }
 sledgehammer_utils = { version = "0.2", optional = true }
 serde = { version = "1.0", features = ["derive"], optional = true }
 
@@ -23,4 +23,5 @@ default = []
 serialize = ["serde"]
 sledgehammer = ["sledgehammer_bindgen", "sledgehammer_utils"]
 web = ["sledgehammer", "wasm-bindgen", "js-sys", "web-sys", "sledgehammer_bindgen/web"]
+binary-protocol = ["sledgehammer"]
 minimal_bindings = []

+ 149 - 141
packages/interpreter/src/sledgehammer_bindings.rs

@@ -1,10 +1,13 @@
 #[cfg(feature = "web")]
 use js_sys::Function;
+#[cfg(feature = "web")]
 use sledgehammer_bindgen::bindgen;
 #[cfg(feature = "web")]
 use web_sys::Node;
 
+#[cfg(feature = "web")]
 pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
+
 #[cfg(feature = "web")]
 #[bindgen(module)]
 mod js {
@@ -285,152 +288,157 @@ mod js {
     }
 }
 
-#[cfg(not(feature = "web"))]
-#[bindgen]
-mod js {
-    const JS_FILE: &str = "./packages/interpreter/src/interpreter.js";
+#[cfg(feature = "binary-protocol")]
+pub mod binary_protocol {
+    use sledgehammer_bindgen::bindgen;
+    pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
 
-    fn mount_to_root() {
-        "{AppendChildren(root, stack.length-1);}"
-    }
-    fn push_root(root: u32) {
-        "{stack.push(nodes[$root$]);}"
-    }
-    fn append_children(id: u32, many: u16) {
-        "{AppendChildren($id$, $many$);}"
-    }
-    fn append_children_to_top(many: u16) {
-        "{
-            root = stack[stack.length-many-1];
-            els = stack.splice(stack.length-many);
-            for (k = 0; k < many; k++) {
-                root.appendChild(els[k]);
-            }
-        }"
-    }
-    fn pop_root() {
-        "{stack.pop();}"
-    }
-    fn replace_with(id: u32, n: u16) {
-        "{root = nodes[$id$]; els = stack.splice(stack.length-$n$); if (root.listening) { listeners.removeAllNonBubbling(root); } root.replaceWith(...els);}"
-    }
-    fn insert_after(id: u32, n: u16) {
-        "{nodes[$id$].after(...stack.splice(stack.length-$n$));}"
-    }
-    fn insert_before(id: u32, n: u16) {
-        "{nodes[$id$].before(...stack.splice(stack.length-$n$));}"
-    }
-    fn remove(id: u32) {
-        "{node = nodes[$id$]; if (node !== undefined) { if (node.listening) { listeners.removeAllNonBubbling(node); } node.remove(); }}"
-    }
-    fn create_raw_text(text: &str) {
-        "{stack.push(document.createTextNode($text$));}"
-    }
-    fn create_text_node(text: &str, id: u32) {
-        "{node = document.createTextNode($text$); nodes[$id$] = node; stack.push(node);}"
-    }
-    fn create_element(element: &'static str<u8, el>) {
-        "{stack.push(document.createElement($element$))}"
-    }
-    fn create_element_ns(element: &'static str<u8, el>, ns: &'static str<u8, namespace>) {
-        "{stack.push(document.createElementNS($ns$, $element$))}"
-    }
-    fn create_placeholder(id: u32) {
-        "{node = document.createElement('pre'); node.hidden = true; stack.push(node); nodes[$id$] = node;}"
-    }
-    fn add_placeholder() {
-        "{node = document.createElement('pre'); node.hidden = true; stack.push(node);}"
-    }
-    fn new_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
-        r#"
-        bubbles = bubbles == 1;
-        node = nodes[id];
-        if(node.listening){
-            node.listening += 1;
-        } else {
-            node.listening = 1;
-        }
-        node.setAttribute('data-dioxus-id', `\${id}`);
+    #[bindgen]
+    mod protocol_js {
+        const JS_FILE: &str = "./packages/interpreter/src/interpreter.js";
 
-        // if this is a mounted listener, we send the event immediately
-        if (event_name === "mounted") {
-            window.ipc.postMessage(
-                serializeIpcMessage("user_event", {
-                    name: event_name,
-                    element: edit.id,
-                    data: null,
-                    bubbles,
-                })
-            );
-        } else {
-            listeners.create(event_name, node, bubbles, (event) => {
-                handler(event, event_name, bubbles, config);
-            });
-        }"#
-    }
-    fn remove_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
-        "{node = nodes[$id$]; node.listening -= 1; node.removeAttribute('data-dioxus-id'); listeners.remove(node, $event_name$, $bubbles$);}"
-    }
-    fn set_text(id: u32, text: &str) {
-        "{nodes[$id$].textContent = $text$;}"
-    }
-    fn set_attribute(id: u32, field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
-        "{node = nodes[$id$]; SetAttributeInner(node, $field$, $value$, $ns$);}"
-    }
-    fn set_top_attribute(field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
-        "{SetAttributeInner(stack[stack.length-1], $field$, $value$, $ns$);}"
-    }
-    fn remove_attribute(id: u32, field: &str<u8, attr>, ns: &str<u8, ns_cache>) {
-        r#"{
-            node = nodes[$id$];
-            if (!ns) {
-                switch (field) {
-                    case "value":
-                        node.value = "";
-                        break;
-                    case "checked":
-                        node.checked = false;
-                        break;
-                    case "selected":
-                        node.selected = false;
-                        break;
-                    case "dangerous_inner_html":
-                        node.innerHTML = "";
-                        break;
-                    default:
-                        node.removeAttribute(field);
-                        break;
+        fn mount_to_root() {
+            "{AppendChildren(root, stack.length-1);}"
+        }
+        fn push_root(root: u32) {
+            "{stack.push(nodes[$root$]);}"
+        }
+        fn append_children(id: u32, many: u16) {
+            "{AppendChildren($id$, $many$);}"
+        }
+        fn append_children_to_top(many: u16) {
+            "{
+                root = stack[stack.length-many-1];
+                els = stack.splice(stack.length-many);
+                for (k = 0; k < many; k++) {
+                    root.appendChild(els[k]);
                 }
-            } else if (ns == "style") {
-                node.style.removeProperty(name);
+            }"
+        }
+        fn pop_root() {
+            "{stack.pop();}"
+        }
+        fn replace_with(id: u32, n: u16) {
+            "{root = nodes[$id$]; els = stack.splice(stack.length-$n$); if (root.listening) { listeners.removeAllNonBubbling(root); } root.replaceWith(...els);}"
+        }
+        fn insert_after(id: u32, n: u16) {
+            "{nodes[$id$].after(...stack.splice(stack.length-$n$));}"
+        }
+        fn insert_before(id: u32, n: u16) {
+            "{nodes[$id$].before(...stack.splice(stack.length-$n$));}"
+        }
+        fn remove(id: u32) {
+            "{node = nodes[$id$]; if (node !== undefined) { if (node.listening) { listeners.removeAllNonBubbling(node); } node.remove(); }}"
+        }
+        fn create_raw_text(text: &str) {
+            "{stack.push(document.createTextNode($text$));}"
+        }
+        fn create_text_node(text: &str, id: u32) {
+            "{node = document.createTextNode($text$); nodes[$id$] = node; stack.push(node);}"
+        }
+        fn create_element(element: &'static str<u8, el>) {
+            "{stack.push(document.createElement($element$))}"
+        }
+        fn create_element_ns(element: &'static str<u8, el>, ns: &'static str<u8, namespace>) {
+            "{stack.push(document.createElementNS($ns$, $element$))}"
+        }
+        fn create_placeholder(id: u32) {
+            "{node = document.createElement('pre'); node.hidden = true; stack.push(node); nodes[$id$] = node;}"
+        }
+        fn add_placeholder() {
+            "{node = document.createElement('pre'); node.hidden = true; stack.push(node);}"
+        }
+        fn new_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
+            r#"
+            bubbles = bubbles == 1;
+            node = nodes[id];
+            if(node.listening){
+                node.listening += 1;
             } else {
-                node.removeAttributeNS(ns, field);
+                node.listening = 1;
             }
-        }"#
-    }
-    fn assign_id(array: &[u8], id: u32) {
-        "{nodes[$id$] = LoadChild($array$);}"
-    }
-    fn hydrate_text(array: &[u8], value: &str, id: u32) {
-        r#"{
-            node = LoadChild($array$);
-            if (node.nodeType == Node.TEXT_NODE) {
-                node.textContent = value;
+            node.setAttribute('data-dioxus-id', `\${id}`);
+    
+            // if this is a mounted listener, we send the event immediately
+            if (event_name === "mounted") {
+                window.ipc.postMessage(
+                    serializeIpcMessage("user_event", {
+                        name: event_name,
+                        element: id,
+                        data: null,
+                        bubbles,
+                    })
+                );
             } else {
-                let text = document.createTextNode(value);
-                node.replaceWith(text);
-                node = text;
-            }
-            nodes[$id$] = node;
-        }"#
-    }
-    fn replace_placeholder(array: &[u8], n: u16) {
-        "{els = stack.splice(stack.length - $n$); node = LoadChild($array$); node.replaceWith(...els);}"
-    }
-    fn load_template(tmpl_id: u16, index: u16, id: u32) {
-        "{node = templates[$tmpl_id$][$index$].cloneNode(true); nodes[$id$] = node; stack.push(node);}"
-    }
-    fn add_templates(tmpl_id: u16, len: u16) {
-        "{templates[$tmpl_id$] = stack.splice(stack.length-$len$);}"
+                listeners.create(event_name, node, bubbles, (event) => {
+                    handler(event, event_name, bubbles, config);
+                });
+            }"#
+        }
+        fn remove_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
+            "{node = nodes[$id$]; node.listening -= 1; node.removeAttribute('data-dioxus-id'); listeners.remove(node, $event_name$, $bubbles$);}"
+        }
+        fn set_text(id: u32, text: &str) {
+            "{nodes[$id$].textContent = $text$;}"
+        }
+        fn set_attribute(id: u32, field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
+            "{node = nodes[$id$]; SetAttributeInner(node, $field$, $value$, $ns$);}"
+        }
+        fn set_top_attribute(field: &str<u8, attr>, value: &str, ns: &str<u8, ns_cache>) {
+            "{SetAttributeInner(stack[stack.length-1], $field$, $value$, $ns$);}"
+        }
+        fn remove_attribute(id: u32, field: &str<u8, attr>, ns: &str<u8, ns_cache>) {
+            r#"{
+                node = nodes[$id$];
+                if (!ns) {
+                    switch (field) {
+                        case "value":
+                            node.value = "";
+                            break;
+                        case "checked":
+                            node.checked = false;
+                            break;
+                        case "selected":
+                            node.selected = false;
+                            break;
+                        case "dangerous_inner_html":
+                            node.innerHTML = "";
+                            break;
+                        default:
+                            node.removeAttribute(field);
+                            break;
+                    }
+                } else if (ns == "style") {
+                    node.style.removeProperty(name);
+                } else {
+                    node.removeAttributeNS(ns, field);
+                }
+            }"#
+        }
+        fn assign_id(array: &[u8], id: u32) {
+            "{nodes[$id$] = LoadChild($array$);}"
+        }
+        fn hydrate_text(array: &[u8], value: &str, id: u32) {
+            r#"{
+                node = LoadChild($array$);
+                if (node.nodeType == Node.TEXT_NODE) {
+                    node.textContent = value;
+                } else {
+                    let text = document.createTextNode(value);
+                    node.replaceWith(text);
+                    node = text;
+                }
+                nodes[$id$] = node;
+            }"#
+        }
+        fn replace_placeholder(array: &[u8], n: u16) {
+            "{els = stack.splice(stack.length - $n$); node = LoadChild($array$); node.replaceWith(...els);}"
+        }
+        fn load_template(tmpl_id: u16, index: u16, id: u32) {
+            "{node = templates[$tmpl_id$][$index$].cloneNode(true); nodes[$id$] = node; stack.push(node);}"
+        }
+        fn add_templates(tmpl_id: u16, len: u16) {
+            "{templates[$tmpl_id$] = stack.splice(stack.length-$len$);}"
+        }
     }
 }

+ 2 - 2
packages/liveview/Cargo.toml

@@ -25,7 +25,7 @@ serde_json = "1.0.91"
 rustc-hash = { workspace = true }
 dioxus-html = { workspace = true, features = ["serialize"] }
 dioxus-core = { workspace = true, features = ["serialize"] }
-dioxus-interpreter-js = { workspace = true, features = ["sledgehammer"] }
+dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] }
 dioxus-hot-reload = { workspace = true, optional = true }
 
 # warp
@@ -54,7 +54,7 @@ salvo = { version = "0.44.1", features = ["affix", "ws"] }
 tower = "0.4.13"
 
 [build-dependencies]
-dioxus-interpreter-js = { workspace = true, features = ["sledgehammer"] }
+dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] }
 minify-js = "0.5.6"
 
 [features]

+ 5 - 5
packages/liveview/build.rs

@@ -1,4 +1,4 @@
-use dioxus_interpreter_js::SLEDGEHAMMER_JS;
+use dioxus_interpreter_js::binary_protocol::SLEDGEHAMMER_JS;
 use minify_js::*;
 use std::io::Write;
 
@@ -55,9 +55,9 @@ fn main() {
     let js = format!("{interpreter}\n{main_js}");
 
     let session = Session::new();
-    let mut out = Vec::new();
-    minify(&session, TopLevelMode::Module, js.as_bytes(), &mut out).unwrap();
-    let minified = String::from_utf8(out).unwrap();
+    // let mut out = Vec::new();
+    // minify(&session, TopLevelMode::Module, js.as_bytes(), &mut out).unwrap();
+    // let minified = String::from_utf8(out).unwrap();
     let mut file = std::fs::File::create("src/minified.js").unwrap();
-    file.write_all(minified.as_bytes()).unwrap();
+    file.write_all(js.as_bytes()).unwrap();
 }

+ 3 - 6
packages/liveview/src/element.rs

@@ -29,7 +29,7 @@ impl RenderedElementBacking for LiveviewElement {
             >,
         >,
     > {
-        let script = format!("return window.interpreter.GetClientRect({});", self.id.0);
+        let script = format!("return getClientRect({});", self.id.0);
 
         let fut = self
             .query
@@ -53,7 +53,7 @@ impl RenderedElementBacking for LiveviewElement {
         behavior: dioxus_html::ScrollBehavior,
     ) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
         let script = format!(
-            "return window.interpreter.ScrollTo({}, {});",
+            "return scrollTo({}, {});",
             self.id.0,
             serde_json::to_string(&behavior).expect("Failed to serialize ScrollBehavior")
         );
@@ -76,10 +76,7 @@ impl RenderedElementBacking for LiveviewElement {
         &self,
         focus: bool,
     ) -> std::pin::Pin<Box<dyn futures_util::Future<Output = dioxus_html::MountedResult<()>>>> {
-        let script = format!(
-            "return window.interpreter.SetFocus({}, {});",
-            self.id.0, focus
-        );
+        let script = format!("return setFocus({}, {});", self.id.0, focus);
 
         let fut = self.query.new_query::<bool>(&script).resolve();
 

+ 16 - 9
packages/liveview/src/main.js

@@ -28,19 +28,26 @@ class IPC {
     };
 
     ws.onmessage = (message) => {
-      if (message.data instanceof ArrayBuffer) {
+      console.log(message.data);
+      const u8view = new Uint8Array(message.data);
+      const binaryFrame = u8view[0] == 1;
+      const messageData = message.data.slice(1)
+      // The first byte tells the shim if this is a binary of text frame
+      if (binaryFrame) {
         // binary frame
-        run_from_bytes(message.data);
-      } else {
+        run_from_bytes(messageData);
+      }
+      else {
         // text frame
+
+        let decoder = new TextDecoder("utf-8");
+
+        // Using decode method to get string output 
+        let str = decoder.decode(messageData);
         // Ignore pongs
-        if (message.data != "__pong__") {
-          const event = JSON.parse(message.data);
+        if (str != "__pong__") {
+          const event = JSON.parse(str);
           switch (event.type) {
-            case "edits":
-              let edits = event.data;
-              window.interpreter.handleEdits(edits);
-              break;
             case "query":
               Function("Eval", `"use strict";${event.data};`)();
               break;

+ 14 - 8
packages/liveview/src/pool.rs

@@ -6,7 +6,7 @@ use crate::{
 };
 use dioxus_core::{prelude::*, BorrowedAttributeValue, Mutations};
 use dioxus_html::{event_bubbles, EventData, HtmlEvent, MountedData};
-use dioxus_interpreter_js::Channel;
+use dioxus_interpreter_js::binary_protocol::Channel;
 use futures_util::{pin_mut, SinkExt, StreamExt};
 use rustc_hash::FxHashMap;
 use serde::Serialize;
@@ -173,7 +173,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
                 match evt.as_ref().map(|o| o.as_deref()) {
                     // respond with a pong every ping to keep the websocket alive
                     Some(Ok(b"__ping__")) => {
-                        // ws.send(b"__pong__".to_vec()).await?;
+                        ws.send(text_frame("__pong__")).await?;
                     }
                     Some(Ok(evt)) => {
                         if let Ok(message) = serde_json::from_str::<IpcMessage>(&String::from_utf8_lossy(evt)) {
@@ -212,7 +212,7 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
 
             // handle any new queries
             Some(query) = query_rx.recv() => {
-                // ws.send(serde_json::to_string(&ClientUpdate::Query(query)).unwrap().into_bytes()).await?;
+                ws.send(text_frame(&serde_json::to_string(&ClientUpdate::Query(query)).unwrap())).await?;
             }
 
             Some(msg) = hot_reload_wait => {
@@ -247,13 +247,19 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li
     }
 }
 
+fn text_frame(text: &str) -> Vec<u8> {
+    let mut bytes = vec![0];
+    bytes.extend(text.as_bytes());
+    bytes
+}
+
 fn add_template(
     template: &Template<'static>,
     channel: &mut Channel,
     templates: &mut FxHashMap<String, u16>,
     max_template_count: &mut u16,
 ) {
-    for (idx, root) in template.roots.iter().enumerate() {
+    for root in template.roots.iter() {
         create_template_node(channel, root);
         templates.insert(template.name.to_owned(), *max_template_count);
     }
@@ -368,16 +374,16 @@ fn apply_edits(
         }
     }
 
-    let bytes: Vec<_> = channel.export_memory().collect();
+    // Add an extra one at the beginning to tell the shim this is a binary frame
+    let mut bytes = vec![1];
+    bytes.extend(channel.export_memory());
     channel.reset();
     Some(bytes)
 }
 
 #[derive(Serialize)]
 #[serde(tag = "type", content = "data")]
-enum ClientUpdate<'a> {
-    #[serde(rename = "edits")]
-    Edits(Mutations<'a>),
+enum ClientUpdate {
     #[serde(rename = "query")]
     Query(String),
 }