diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 0fc26674c..53df88759 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -13,6 +13,10 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +dioxus-html = { path = "../html", features = ["serialize"], version = "^0.3.0" } +dioxus-core = { path = "../core", features = ["serialize"], version = "^0.3.0" } +dioxus-interpreter-js = { path = "../interpreter", version = "0.3.0" } + tokio = { version = "1.23.0", features = ["full"] } futures-util = { version = "0.3.25", default-features = false, features = [ "sink", @@ -25,9 +29,7 @@ serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" tokio-util = { version = "0.7.4", features = ["full"] } -dioxus-html = { path = "../html", features = ["serialize"], version = "^0.3.0" } -dioxus-core = { path = "../core", features = ["serialize"], version = "^0.3.0" } -dioxus-interpreter-js = { path = "../interpreter", version = "0.3.0" } +interprocess = { version = "1.2.1", optional = true } # warp warp = { version = "0.3.3", optional = true } @@ -56,8 +58,9 @@ salvo = { version = "0.37.7", features = ["affix", "ws"] } tower = "0.4.13" [features] -default = [] +default = ["hot-reload"] # actix = ["actix-files", "actix-web", "actix-ws"] +hot-reload = ["interprocess"] [[example]] name = "axum" diff --git a/packages/liveview/src/hot_reload.rs b/packages/liveview/src/hot_reload.rs new file mode 100644 index 000000000..8385879e0 --- /dev/null +++ b/packages/liveview/src/hot_reload.rs @@ -0,0 +1,31 @@ +#![allow(dead_code)] + +use dioxus_core::Template; + +use interprocess::local_socket::LocalSocketStream; +use std::io::{BufRead, BufReader}; +use tokio::sync::mpsc::UnboundedSender; + +pub(crate) fn init(proxy: UnboundedSender>) { + std::thread::spawn(move || { + let temp_file = std::env::temp_dir().join("@dioxusin"); + if let Ok(socket) = LocalSocketStream::connect(temp_file.as_path()) { + let mut buf_reader = BufReader::new(socket); + loop { + let mut buf = String::new(); + match buf_reader.read_line(&mut buf) { + Ok(_) => { + let template: Template<'static> = + serde_json::from_str(Box::leak(buf.into_boxed_str())).unwrap(); + proxy.send(template).unwrap(); + } + Err(err) => { + if err.kind() != std::io::ErrorKind::WouldBlock { + break; + } + } + } + } + } + }); +} diff --git a/packages/liveview/src/lib.rs b/packages/liveview/src/lib.rs index ffddf0272..6c4428c3c 100644 --- a/packages/liveview/src/lib.rs +++ b/packages/liveview/src/lib.rs @@ -18,6 +18,8 @@ pub mod adapters { pub use adapters::*; +#[cfg(all(feature = "hot-reload", debug_assertions))] +mod hot_reload; pub mod pool; use futures_util::{SinkExt, StreamExt}; pub use pool::*; diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index a25673d84..0f7598333 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -103,6 +103,13 @@ pub async fn run( where T: Send + 'static, { + #[cfg(all(feature = "hot-reload", debug_assertions))] + let mut hot_reload_rx = { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + crate::hot_reload::init(tx); + rx + }; + let mut vdom = VirtualDom::new_with_props(app, props); // todo: use an efficient binary packed format for this @@ -122,6 +129,11 @@ where } loop { + #[cfg(all(feature = "hot-reload", debug_assertions))] + let hot_reload_wait = hot_reload_rx.recv(); + #[cfg(not(all(feature = "hot-reload", debug_assertions)))] + let hot_reload_wait = std::future::pending(); + tokio::select! { // poll any futures or suspense _ = vdom.wait_for_work() => {} @@ -138,6 +150,12 @@ where None => return Ok(()), } } + + new_template = hot_reload_wait => { + if let Some(new_template) = new_template { + vdom.replace_template(new_template); + } + } } let edits = vdom