From 8f5d5a1cd3a8e9971a75024ec92bdba951221a69 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 19 Mar 2024 19:48:14 -0700 Subject: [PATCH 1/7] Fix: css hotreloading being invalidated, watcher not watching cargo/dioxus tomls, add feature to restore window state --- Cargo.lock | 11 ++ packages/cli/src/builder.rs | 71 +++++++----- packages/cli/src/server/desktop/mod.rs | 26 ++++- packages/cli/src/server/mod.rs | 8 +- packages/cli/src/server/output.rs | 2 +- packages/desktop/Cargo.toml | 1 + packages/desktop/src/app.rs | 151 ++++++++++++++++++++++++- packages/desktop/src/config.rs | 15 ++- packages/desktop/src/ipc.rs | 7 ++ packages/desktop/src/launch.rs | 5 + packages/desktop/src/menubar.rs | 10 ++ packages/desktop/src/webview.rs | 21 +++- packages/hot-reload/src/lib.rs | 44 ++++--- packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/native.js | 2 +- packages/interpreter/src/ts/native.ts | 38 ++++++- 16 files changed, 343 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32855b311d..3cae97e67b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2174,6 +2174,7 @@ dependencies = [ "rustc-hash", "serde", "serde_json", + "signal-hook", "slab", "tao", "thiserror", @@ -7868,6 +7869,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" diff --git a/packages/cli/src/builder.rs b/packages/cli/src/builder.rs index 638572fdaa..9108855aed 100644 --- a/packages/cli/src/builder.rs +++ b/packages/cli/src/builder.rs @@ -335,43 +335,14 @@ pub fn build_desktop( let _manganis_support = ManganisSupportGuard::default(); let _guard = AssetConfigDropGuard::new(); - let mut cmd = subprocess::Exec::cmd("cargo") + let cmd = subprocess::Exec::cmd("cargo") .set_rust_flags(rust_flags) .env("CARGO_TARGET_DIR", &config.target_dir) .cwd(&config.crate_dir) .arg("build") .arg("--message-format=json-render-diagnostics"); - if config.release { - cmd = cmd.arg("--release"); - } - if config.verbose { - cmd = cmd.arg("--verbose"); - } else { - cmd = cmd.arg("--quiet"); - } - - if config.custom_profile.is_some() { - let custom_profile = config.custom_profile.as_ref().unwrap(); - cmd = cmd.arg("--profile").arg(custom_profile); - } - - if config.features.is_some() { - let features_str = config.features.as_ref().unwrap().join(" "); - cmd = cmd.arg("--features").arg(features_str); - } - - if let Some(target) = &config.target { - cmd = cmd.arg("--target").arg(target); - } - - cmd = cmd.args(&config.cargo_args); - - let cmd = match &config.executable { - ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), - ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), - ExecutableType::Example(name) => cmd.arg("--example").arg(name), - }; + let cmd = apply_config_build_flags(config, cmd); let warning_messages = prettier_build(cmd)?; @@ -450,6 +421,44 @@ pub fn build_desktop( }) } +/// Apply the build flags to the command +/// +/// This is factored out so it can be used in both `build` and `run` +fn apply_config_build_flags(config: &CrateConfig, mut cmd: subprocess::Exec) -> subprocess::Exec { + if config.release { + cmd = cmd.arg("--release"); + } + if config.verbose { + cmd = cmd.arg("--verbose"); + } else { + cmd = cmd.arg("--quiet"); + } + + if config.custom_profile.is_some() { + let custom_profile = config.custom_profile.as_ref().unwrap(); + cmd = cmd.arg("--profile").arg(custom_profile); + } + + if config.features.is_some() { + let features_str = config.features.as_ref().unwrap().join(" "); + cmd = cmd.arg("--features").arg(features_str); + } + + if let Some(target) = &config.target { + cmd = cmd.arg("--target").arg(target); + } + + cmd = cmd.args(&config.cargo_args); + + let cmd = match &config.executable { + ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), + ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), + ExecutableType::Example(name) => cmd.arg("--example").arg(name), + }; + + cmd +} + struct CargoBuildResult { warnings: Vec, output_location: Option, diff --git a/packages/cli/src/server/desktop/mod.rs b/packages/cli/src/server/desktop/mod.rs index d5b5c7b110..45ccb76cfb 100644 --- a/packages/cli/src/server/desktop/mod.rs +++ b/packages/cli/src/server/desktop/mod.rs @@ -111,6 +111,7 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<() .exec() .unwrap(); let target_dir = metadata.target_directory.as_std_path(); + let _ = create_dir_all(target_dir); // `_all` is for good measure and future-proofness. let path = target_dir.join("dioxusin"); clear_paths(&path); @@ -141,6 +142,7 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<() .flat_map(|v| v.templates.values().copied()) .collect() }; + for template in templates { if !send_msg( HotReloadMsg::UpdateTemplate(template), @@ -282,7 +284,29 @@ impl DesktopPlatform { config: &CrateConfig, rust_flags: Option, ) -> Result { - self.currently_running_child.0.kill()?; + // Gracefully shtudown the desktop app + // It might have a receiver to do some cleanup stuff + let pid = self.currently_running_child.0.id(); + + // on unix, we can send a signal to the process to shut down + #[cfg(unix)] + { + _ = Command::new("kill") + .args(["-s", "TERM", &pid.to_string()]) + .spawn(); + } + + // on windows, use the `taskkill` command + #[cfg(windows)] + { + _ = Command::new("taskkill") + .args(["/F", "/PID", &pid.to_string()]) + .spawn(); + } + + // Todo: add a timeout here to kill the process if it doesn't shut down within a reasonable time + self.currently_running_child.0.wait()?; + let (child, result) = start_desktop(config, self.skip_assets, rust_flags)?; self.currently_running_child = child; Ok(result) diff --git a/packages/cli/src/server/mod.rs b/packages/cli/src/server/mod.rs index 332fbf909e..b62d821080 100644 --- a/packages/cli/src/server/mod.rs +++ b/packages/cli/src/server/mod.rs @@ -56,9 +56,14 @@ async fn setup_file_watcher Result + Send + 'static>( // file watcher: check file change let mut allow_watch_path = config.dioxus_config.web.watcher.watch_path.clone(); - // Extend the watch path to include the assets directory - this is so we can hotreload CSS and other assets + // Extend the watch path to include the assets directory - this is so we can hotreload CSS and other assets by default allow_watch_path.push(config.dioxus_config.application.asset_dir.clone()); + // Extend the watch path to include Cargo.toml and Dioxus.toml + allow_watch_path.push("Cargo.toml".to_string().into()); + allow_watch_path.push("Dioxus.toml".to_string().into()); + allow_watch_path.dedup(); + // Create the file watcher let mut watcher = notify::recommended_watcher({ let watcher_config = config.clone(); @@ -66,7 +71,6 @@ async fn setup_file_watcher Result + Send + 'static>( let Ok(e) = info else { return; }; - watch_event( e, &mut last_update_time, diff --git a/packages/cli/src/server/output.rs b/packages/cli/src/server/output.rs index 3edd94dbaa..74c558cc55 100644 --- a/packages/cli/src/server/output.rs +++ b/packages/cli/src/server/output.rs @@ -112,7 +112,7 @@ pub fn print_console_info( .watch_path .iter() .cloned() - .chain(Some(config.dioxus_config.application.asset_dir.clone())) + .chain(["Cargo.toml", "Dioxus.toml"].iter().map(PathBuf::from)) .map(|f| f.display().to_string()) .collect::>() .join(", ") diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index baa7a4b120..77942c4ffc 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -49,6 +49,7 @@ futures-util = { workspace = true } urlencoding = "2.1.2" async-trait = "0.1.68" tao = { version = "0.26.1", features = ["rwh_05"] } +signal-hook = "0.3.17" [target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux",target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies] global-hotkey = "0.5.0" diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 65d732806d..ff20a8bf4d 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -18,6 +18,7 @@ use std::{ sync::Arc, }; use tao::{ + dpi::{PhysicalPosition, PhysicalSize}, event::Event, event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget}, window::WindowId, @@ -35,6 +36,7 @@ pub(crate) struct App { pub(crate) is_visible_before_start: bool, pub(crate) window_behavior: WindowCloseBehaviour, pub(crate) webviews: HashMap, + pub(crate) float_all: bool, /// This single blob of state is shared between all the windows so they have access to the runtime state /// @@ -61,6 +63,7 @@ impl App { webviews: HashMap::new(), control_flow: ControlFlow::Wait, unmounted_dom: Cell::new(Some(virtual_dom)), + float_all: cfg!(debug_assertions), cfg: Cell::new(Some(cfg)), shared: Rc::new(SharedContext { event_handlers: WindowEventHandlers::default(), @@ -78,6 +81,10 @@ impl App { #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] app.set_global_hotkey_handler(); + // Wire up the menubar receiver - this way any component can key into the menubar actions + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + app.set_menubar_receiver(); + // Allow hotreloading to work - but only in debug mode #[cfg(all( feature = "hot-reload", @@ -87,6 +94,10 @@ impl App { ))] app.connect_hotreload(); + #[cfg(debug_assertions)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + app.connect_preserve_window_state_handler(); + (event_loop, app) } @@ -102,6 +113,20 @@ impl App { self.shared.shortcut_manager.call_handlers(event); } + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + pub fn handle_menu_event(&mut self, event: muda::MenuEvent) { + if event.id() == "dioxus-float-top" { + for webview in self.webviews.values() { + webview + .desktop_context + .window + .set_always_on_top(self.float_all); + } + } + + self.float_all = !self.float_all; + } + #[cfg(all( feature = "hot-reload", debug_assertions, @@ -109,7 +134,11 @@ impl App { not(target_os = "ios") ))] pub fn connect_hotreload(&self) { - dioxus_hot_reload::connect({ + let Ok(cfg) = dioxus_cli_config::CURRENT_CONFIG.as_ref() else { + return; + }; + + dioxus_hot_reload::connect_at(cfg.target_dir.join("dioxusin"), { let proxy = self.shared.proxy.clone(); move |template| { let _ = proxy.send_event(UserWindowEvent::HotReloadEvent(template)); @@ -169,6 +198,10 @@ impl App { let webview = WebviewInstance::new(cfg, virtual_dom, self.shared.clone()); + // And then attempt to resume from state + #[cfg(debug_assertions)] + self.resume_from_state(&webview); + let id = webview.desktop_context.window.id(); self.webviews.insert(id, webview); } @@ -286,6 +319,10 @@ impl App { webview.dom.replace_template(template); webview.poll_vdom(); } + + for webview in self.webviews.values_mut() { + webview.kick_stylsheets(false); + } } dioxus_hot_reload::HotReloadMsg::Shutdown => { self.control_flow = ControlFlow::Exit; @@ -293,7 +330,7 @@ impl App { dioxus_hot_reload::HotReloadMsg::UpdateAsset(_) => { for webview in self.webviews.values_mut() { - webview.kick_stylsheets(); + webview.kick_stylsheets(true); } } } @@ -356,6 +393,116 @@ impl App { _ = receiver.send_event(UserWindowEvent::GlobalHotKeyEvent(t)); })); } + + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + fn set_menubar_receiver(&self) { + let receiver = self.shared.proxy.clone(); + + // The event loop becomes the menu receiver + // This means we don't need to poll the receiver on every tick - we just get the events as they come in + // This is a bit more efficient than the previous implementation, but if someone else sets a handler, the + // receiver will become inert. + muda::MenuEvent::set_event_handler(Some(move |t| { + // todo: should we unset the event handler when the app shuts down? + _ = receiver.send_event(UserWindowEvent::MudaMenuEvent(t)); + })); + } + + /// Do our best to preserve state about the window when the event loop is destroyed + /// + /// This will attempt to save the window position, size, and monitor into the environment before + /// closing. This way, when the app is restarted, it can attempt to restore the window to the same + /// position and size it was in before, making a better DX. + pub(crate) fn handle_loop_destroyed(&self) { + #[cfg(debug_assertions)] + self.persist_window_state(); + } + + #[cfg(debug_assertions)] + fn persist_window_state(&self) { + if let Some(webview) = self.webviews.values().next() { + let window = &webview.desktop_context.window; + + let monitor = window.current_monitor().unwrap(); + let position = window.outer_position().unwrap(); + let size = window.outer_size(); + + let x = position.x; + let y = position.y; + + // This is to work around a bug in how tao handles inner_size on macOS + // We *want* to use inner_size, but that's currently broken, so we use outer_size instead and then an adjustment + // + // https://github.com/tauri-apps/tao/issues/889 + let adjustment = match window.is_decorated() { + true if cfg!(target_os = "macos") => 56, + _ => 0, + }; + + let state = PreservedWindowState { + x, + y, + width: size.width.max(200), + height: size.height.saturating_sub(adjustment).max(200), + monitor: monitor.name().unwrap().to_string(), + }; + + if let Ok(state) = serde_json::to_string(&state) { + // Write this to the target dir so we can pick back up in resume_from_state + dioxus_cli_config::CURRENT_CONFIG.as_ref().map(|cfg| { + let path = cfg.target_dir.join("window_state.json"); + _ = std::fs::write(path, state); + }); + } + } + } + + // Write this to the target dir so we can pick back up + #[cfg(debug_assertions)] + fn resume_from_state(&mut self, webview: &WebviewInstance) { + if let Ok(cfg) = dioxus_cli_config::CURRENT_CONFIG.as_ref() { + let path = cfg.target_dir.join("window_state.json"); + if let Some(state) = std::fs::read_to_string(path).ok() { + if let Ok(state) = serde_json::from_str::(&state) { + let window = &webview.desktop_context.window; + let position = (state.x, state.y); + let size = (state.width, state.height); + let monitor = state.monitor.as_str(); + window.set_outer_position(PhysicalPosition::new(position.0, position.1)); + window.set_inner_size(PhysicalSize::new(size.0, size.1)); + } + } + } + } + + /// Wire up a receiver to sigkill that lets us preserve the window state + /// Whenever sigkill is sent, we shut down the app and save the window state + #[cfg(debug_assertions)] + fn connect_preserve_window_state_handler(&self) { + // Wire up the trap + let target = self.shared.proxy.clone(); + std::thread::spawn(move || { + use signal_hook::consts::{SIGINT, SIGTERM}; + let mut sigkill = signal_hook::iterator::Signals::new(&[SIGTERM, SIGINT]); + if let Ok(mut sigkill) = sigkill { + for _ in sigkill.forever() { + target.send_event(UserWindowEvent::Shutdown); + + // give it a moment for the event to be processed + std::thread::sleep(std::time::Duration::from_secs(1)); + } + } + }); + } +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct PreservedWindowState { + x: i32, + y: i32, + width: u32, + height: u32, + monitor: String, } /// Different hide implementations per platform diff --git a/packages/desktop/src/config.rs b/packages/desktop/src/config.rs index 10e2dcb748..a31008e745 100644 --- a/packages/desktop/src/config.rs +++ b/packages/desktop/src/config.rs @@ -41,12 +41,15 @@ impl Config { /// Initializes a new `WindowBuilder` with default values. #[inline] pub fn new() -> Self { - let window = WindowBuilder::new().with_title( - dioxus_cli_config::CURRENT_CONFIG - .as_ref() - .map(|c| c.dioxus_config.application.name.clone()) - .unwrap_or("Dioxus App".to_string()), - ); + let window: WindowBuilder = WindowBuilder::new() + .with_title( + dioxus_cli_config::CURRENT_CONFIG + .as_ref() + .map(|c| c.dioxus_config.application.name.clone()) + .unwrap_or("Dioxus App".to_string()), + ) + // During development we want the window to be on top so we can see it while we work + .with_always_on_top(cfg!(debug_assertions)); Self { window, diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index a3212b166a..ac7ac9180c 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -1,12 +1,16 @@ use serde::{Deserialize, Serialize}; use tao::window::WindowId; +#[non_exhaustive] #[derive(Debug, Clone)] pub enum UserWindowEvent { /// A global hotkey event #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] GlobalHotKeyEvent(global_hotkey::GlobalHotKeyEvent), + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + MudaMenuEvent(muda::MenuEvent), + /// Poll the virtualdom Poll(WindowId), @@ -27,6 +31,9 @@ pub enum UserWindowEvent { /// Close a given window (could be any window!) CloseWindow(WindowId), + + /// Gracefully shutdown the entire app + Shutdown, } /// A message struct that manages the communication between the webview and the eventloop code diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 16c9c96b9b..c3962f43db 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -20,6 +20,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf match window_event { Event::NewEvents(StartCause::Init) => app.handle_start_cause_init(), + Event::LoopDestroyed => app.handle_loop_destroyed(), Event::WindowEvent { event, window_id, .. } => match event { @@ -32,10 +33,14 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf UserWindowEvent::Poll(id) => app.poll_vdom(id), UserWindowEvent::NewWindow => app.handle_new_window(), UserWindowEvent::CloseWindow(id) => app.handle_close_msg(id), + UserWindowEvent::Shutdown => app.control_flow = tao::event_loop::ControlFlow::Exit, #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] UserWindowEvent::GlobalHotKeyEvent(evnt) => app.handle_global_hotkey(evnt), + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt), + #[cfg(all( feature = "hot-reload", debug_assertions, diff --git a/packages/desktop/src/menubar.rs b/packages/desktop/src/menubar.rs index eb02cce4b7..c434bc7871 100644 --- a/packages/desktop/src/menubar.rs +++ b/packages/desktop/src/menubar.rs @@ -93,6 +93,16 @@ mod desktop_platforms { .append_items(&[&MenuItem::new("Toggle Developer Tools", true, None)]) .unwrap(); + // By default we float the window on top in dev mode, but let the user disable it + help_menu + .append_items(&[&MenuItem::with_id( + "dioxus-float-top", + "Float on Top (dev mode only)", + true, + None, + )]) + .unwrap(); + menu.append_items(&[&window_menu, &edit_menu, &help_menu]) .unwrap(); diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index d32bfe2550..1d4659e39f 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -225,11 +225,20 @@ impl WebviewInstance { } #[cfg(all(feature = "hot-reload", debug_assertions))] - pub fn kick_stylsheets(&self) { - // run eval in the webview to kick the stylesheets by appending a query string - // we should do something less clunky than this - _ = self.desktop_context - .webview - .evaluate_script("document.querySelectorAll('link[rel=\"stylesheet\"]').forEach((el) => el.href = el.href + \"?\" + Math.random());"); + pub fn kick_stylsheets(&self, now: bool) { + if now { + // run eval in the webview to kick the stylesheets by appending a query string + // we should do something less clunky than this + _ = self + .desktop_context + .webview + .evaluate_script("window.interpreter.kickAllStylesheetsOnPage()"); + } else { + // Just set the flag to kick the stylesheets on the next render + _ = self + .desktop_context + .webview + .evaluate_script("window.interpreter.kickStylesheets = true"); + } } } diff --git a/packages/hot-reload/src/lib.rs b/packages/hot-reload/src/lib.rs index b912c6e195..3f5c9abf9b 100644 --- a/packages/hot-reload/src/lib.rs +++ b/packages/hot-reload/src/lib.rs @@ -29,32 +29,40 @@ pub enum HotReloadMsg { } /// Connect to the hot reloading listener. The callback provided will be called every time a template change is detected -pub fn connect(mut callback: impl FnMut(HotReloadMsg) + Send + 'static) { - std::thread::spawn(move || { - // get the cargo manifest directory, where the target dir lives - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); +pub fn connect(callback: impl FnMut(HotReloadMsg) + Send + 'static) { + let Ok(_manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") else { + return; + }; - // walk the path until we a find a socket named `dioxusin` inside that folder's target directory - loop { - let maybe = path.join("target").join("dioxusin"); + // get the cargo manifest directory, where the target dir lives + let mut path = PathBuf::from(_manifest_dir); - if maybe.exists() { - path = maybe; - break; - } + // walk the path until we a find a socket named `dioxusin` inside that folder's target directory + loop { + let maybe = path.join("target").join("dioxusin"); - // It's likely we're running under just cargo and not dx - path = match path.parent() { - Some(parent) => parent.to_path_buf(), - None => return, - }; + if maybe.exists() { + path = maybe; + break; } + // It's likely we're running under just cargo and not dx + path = match path.parent() { + Some(parent) => parent.to_path_buf(), + None => return, + }; + } + + connect_at(path, callback); +} + +pub fn connect_at(socket: PathBuf, mut callback: impl FnMut(HotReloadMsg) + Send + 'static) { + std::thread::spawn(move || { // There might be a socket since the we're not running under the hot reloading server - let Ok(socket) = LocalSocketStream::connect(path.clone()) else { + let Ok(socket) = LocalSocketStream::connect(socket.clone()) else { println!( "could not find hot reloading server at {:?}, make sure it's running", - path + socket ); return; }; diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index 9fe9e2627b..97a0a841c3 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -12655652627 \ No newline at end of file +14614912926 \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index 4df91d3b99..70c5e2c797 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retriveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;liveview;constructor(editsPath){super();this.editsPath=editsPath}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>this.run_from_bytes(bytes));this.waitForRequest(headless)})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;kickStylesheets;liveview;constructor(editsPath){super();this.editsPath=editsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>{if(this.run_from_bytes(bytes),this.kickStylesheets)this.kickAllStylesheetsOnPage(),this.kickStylesheets=!1});this.waitForRequest(headless)})}kickAllStylesheetsOnPage(){const forceKicked=this.kickStylesheets===!1;console.log("Kicking all stylesheets on the page",forceKicked);let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i this.run_from_bytes(bytes)); + requestAnimationFrame(() => { + // @ts-ignore + this.run_from_bytes(bytes); + + // If we're hotreloading, we want to try and kick the stylesheets so they're not stale after templates change + if (this.kickStylesheets) { + this.kickAllStylesheetsOnPage(); + this.kickStylesheets = false; + } + }); } this.waitForRequest(headless); }); } + kickAllStylesheetsOnPage() { + // If this function is being called and we have not explicitly set kickStylesheets to true, then we should + // force kick the stylesheets, regardless if they have a dioxus attribute or not + // This happens when any hotreload happens. + const forceKicked = (this.kickStylesheets === false); + console.log("Kicking all stylesheets on the page", forceKicked); + + let stylesheets = document.querySelectorAll("link[rel=stylesheet]"); + + for (let i = 0; i < stylesheets.length; i++) { + let sheet = stylesheets[i] as HTMLLinkElement; + + if (forceKicked) { + sheet.setAttribute("data-dioxus-kick", "true"); + } + + // templates won't have the kick attribute, meaning only they need to be kicked + // This lets us skip templates that haven't changed + if (!sheet.hasAttribute("data-dioxus-kick") || forceKicked) { + sheet.href = sheet.href + "?" + Math.random(); + } + } + } + // A liveview only function // Desktop will intercept the event before it hits this async readFiles(target: HTMLInputElement, contents: SerializedEvent, bubbles: boolean, realId: NodeId, name: string) { From accc355bae54b7b1cad7ce8eddd0872ca35c13f0 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 19 Mar 2024 19:59:35 -0700 Subject: [PATCH 2/7] Make clappy hippier --- packages/cli/src/builder.rs | 71 ++++++++++++++++--------------------- packages/desktop/src/app.rs | 13 +++---- 2 files changed, 38 insertions(+), 46 deletions(-) diff --git a/packages/cli/src/builder.rs b/packages/cli/src/builder.rs index 9108855aed..638572fdaa 100644 --- a/packages/cli/src/builder.rs +++ b/packages/cli/src/builder.rs @@ -335,14 +335,43 @@ pub fn build_desktop( let _manganis_support = ManganisSupportGuard::default(); let _guard = AssetConfigDropGuard::new(); - let cmd = subprocess::Exec::cmd("cargo") + let mut cmd = subprocess::Exec::cmd("cargo") .set_rust_flags(rust_flags) .env("CARGO_TARGET_DIR", &config.target_dir) .cwd(&config.crate_dir) .arg("build") .arg("--message-format=json-render-diagnostics"); - let cmd = apply_config_build_flags(config, cmd); + if config.release { + cmd = cmd.arg("--release"); + } + if config.verbose { + cmd = cmd.arg("--verbose"); + } else { + cmd = cmd.arg("--quiet"); + } + + if config.custom_profile.is_some() { + let custom_profile = config.custom_profile.as_ref().unwrap(); + cmd = cmd.arg("--profile").arg(custom_profile); + } + + if config.features.is_some() { + let features_str = config.features.as_ref().unwrap().join(" "); + cmd = cmd.arg("--features").arg(features_str); + } + + if let Some(target) = &config.target { + cmd = cmd.arg("--target").arg(target); + } + + cmd = cmd.args(&config.cargo_args); + + let cmd = match &config.executable { + ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), + ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), + ExecutableType::Example(name) => cmd.arg("--example").arg(name), + }; let warning_messages = prettier_build(cmd)?; @@ -421,44 +450,6 @@ pub fn build_desktop( }) } -/// Apply the build flags to the command -/// -/// This is factored out so it can be used in both `build` and `run` -fn apply_config_build_flags(config: &CrateConfig, mut cmd: subprocess::Exec) -> subprocess::Exec { - if config.release { - cmd = cmd.arg("--release"); - } - if config.verbose { - cmd = cmd.arg("--verbose"); - } else { - cmd = cmd.arg("--quiet"); - } - - if config.custom_profile.is_some() { - let custom_profile = config.custom_profile.as_ref().unwrap(); - cmd = cmd.arg("--profile").arg(custom_profile); - } - - if config.features.is_some() { - let features_str = config.features.as_ref().unwrap().join(" "); - cmd = cmd.arg("--features").arg(features_str); - } - - if let Some(target) = &config.target { - cmd = cmd.arg("--target").arg(target); - } - - cmd = cmd.args(&config.cargo_args); - - let cmd = match &config.executable { - ExecutableType::Binary(name) => cmd.arg("--bin").arg(name), - ExecutableType::Lib(name) => cmd.arg("--lib").arg(name), - ExecutableType::Example(name) => cmd.arg("--example").arg(name), - }; - - cmd -} - struct CargoBuildResult { warnings: Vec, output_location: Option, diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index ff20a8bf4d..bba743168c 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -449,10 +449,10 @@ impl App { if let Ok(state) = serde_json::to_string(&state) { // Write this to the target dir so we can pick back up in resume_from_state - dioxus_cli_config::CURRENT_CONFIG.as_ref().map(|cfg| { + if let Ok(cfg) = dioxus_cli_config::CURRENT_CONFIG.as_ref() { let path = cfg.target_dir.join("window_state.json"); _ = std::fs::write(path, state); - }); + } } } } @@ -462,12 +462,11 @@ impl App { fn resume_from_state(&mut self, webview: &WebviewInstance) { if let Ok(cfg) = dioxus_cli_config::CURRENT_CONFIG.as_ref() { let path = cfg.target_dir.join("window_state.json"); - if let Some(state) = std::fs::read_to_string(path).ok() { + if let Ok(state) = std::fs::read_to_string(path) { if let Ok(state) = serde_json::from_str::(&state) { let window = &webview.desktop_context.window; let position = (state.x, state.y); let size = (state.width, state.height); - let monitor = state.monitor.as_str(); window.set_outer_position(PhysicalPosition::new(position.0, position.1)); window.set_inner_size(PhysicalSize::new(size.0, size.1)); } @@ -483,10 +482,12 @@ impl App { let target = self.shared.proxy.clone(); std::thread::spawn(move || { use signal_hook::consts::{SIGINT, SIGTERM}; - let mut sigkill = signal_hook::iterator::Signals::new(&[SIGTERM, SIGINT]); + let sigkill = signal_hook::iterator::Signals::new([SIGTERM, SIGINT]); if let Ok(mut sigkill) = sigkill { for _ in sigkill.forever() { - target.send_event(UserWindowEvent::Shutdown); + if target.send_event(UserWindowEvent::Shutdown).is_err() { + std::process::exit(0); + } // give it a moment for the event to be processed std::thread::sleep(std::time::Duration::from_secs(1)); From 2787ff2e063c9f65a7d59fbbd17736ff95d1b786 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 19 Mar 2024 20:02:11 -0700 Subject: [PATCH 3/7] remove console log --- packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/native.js | 2 +- packages/interpreter/src/ts/native.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index 97a0a841c3..3b70d472fe 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -14614912926 \ No newline at end of file +14504657752 \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index 70c5e2c797..210007058d 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retriveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;kickStylesheets;liveview;constructor(editsPath){super();this.editsPath=editsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>{if(this.run_from_bytes(bytes),this.kickStylesheets)this.kickAllStylesheetsOnPage(),this.kickStylesheets=!1});this.waitForRequest(headless)})}kickAllStylesheetsOnPage(){const forceKicked=this.kickStylesheets===!1;console.log("Kicking all stylesheets on the page",forceKicked);let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;kickStylesheets;liveview;constructor(editsPath){super();this.editsPath=editsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>{if(this.run_from_bytes(bytes),this.kickStylesheets)this.kickAllStylesheetsOnPage(),this.kickStylesheets=!1});this.waitForRequest(headless)})}kickAllStylesheetsOnPage(){const forceKicked=this.kickStylesheets===!1;let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i Date: Tue, 19 Mar 2024 20:48:41 -0700 Subject: [PATCH 4/7] use simpler css invalidator --- packages/desktop/src/app.rs | 6 +----- packages/desktop/src/webview.rs | 22 +++++++--------------- packages/dioxus/src/lib.rs | 2 ++ packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/native.js | 2 +- packages/interpreter/src/ts/native.ts | 23 ++++------------------- 6 files changed, 16 insertions(+), 41 deletions(-) diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index bba743168c..37ec93cd8a 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -319,10 +319,6 @@ impl App { webview.dom.replace_template(template); webview.poll_vdom(); } - - for webview in self.webviews.values_mut() { - webview.kick_stylsheets(false); - } } dioxus_hot_reload::HotReloadMsg::Shutdown => { self.control_flow = ControlFlow::Exit; @@ -330,7 +326,7 @@ impl App { dioxus_hot_reload::HotReloadMsg::UpdateAsset(_) => { for webview in self.webviews.values_mut() { - webview.kick_stylsheets(true); + webview.kick_stylsheets(); } } } diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index 1d4659e39f..4a9b836558 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -225,20 +225,12 @@ impl WebviewInstance { } #[cfg(all(feature = "hot-reload", debug_assertions))] - pub fn kick_stylsheets(&self, now: bool) { - if now { - // run eval in the webview to kick the stylesheets by appending a query string - // we should do something less clunky than this - _ = self - .desktop_context - .webview - .evaluate_script("window.interpreter.kickAllStylesheetsOnPage()"); - } else { - // Just set the flag to kick the stylesheets on the next render - _ = self - .desktop_context - .webview - .evaluate_script("window.interpreter.kickStylesheets = true"); - } + pub fn kick_stylsheets(&self) { + // run eval in the webview to kick the stylesheets by appending a query string + // we should do something less clunky than this + _ = self + .desktop_context + .webview + .evaluate_script("window.interpreter.kickAllStylesheetsOnPage()"); } } diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 188a303110..e52c6ddb62 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -9,6 +9,8 @@ pub use dioxus_core; #[cfg_attr(docsrs, doc(cfg(feature = "launch")))] mod launch; +pub use launch::launch; + #[cfg(feature = "hooks")] #[cfg_attr(docsrs, doc(cfg(feature = "hooks")))] pub use dioxus_hooks as hooks; diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index 3b70d472fe..986574271a 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -14504657752 \ No newline at end of file +13799725074 \ No newline at end of file diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index 210007058d..28791835b2 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retriveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;kickStylesheets;liveview;constructor(editsPath){super();this.editsPath=editsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>{if(this.run_from_bytes(bytes),this.kickStylesheets)this.kickAllStylesheetsOnPage(),this.kickStylesheets=!1});this.waitForRequest(headless)})}kickAllStylesheetsOnPage(){const forceKicked=this.kickStylesheets===!1;let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;kickStylesheets;liveview;constructor(editsPath){super();this.editsPath=editsPath,this.kickStylesheets=!1}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>{this.run_from_bytes(bytes)});this.waitForRequest(headless)})}kickAllStylesheetsOnPage(){let stylesheets=document.querySelectorAll("link[rel=stylesheet]");for(let i=0;i{sheet.href=sheet.href+"?"+Math.random()})}}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i { // @ts-ignore - this.run_from_bytes(bytes); - - // If we're hotreloading, we want to try and kick the stylesheets so they're not stale after templates change - if (this.kickStylesheets) { - this.kickAllStylesheetsOnPage(); - this.kickStylesheets = false; - } + this.run_from_bytes(bytes) }); } this.waitForRequest(headless); @@ -294,22 +288,13 @@ export class NativeInterpreter extends JSChannel_ { // If this function is being called and we have not explicitly set kickStylesheets to true, then we should // force kick the stylesheets, regardless if they have a dioxus attribute or not // This happens when any hotreload happens. - const forceKicked = (this.kickStylesheets === false); - let stylesheets = document.querySelectorAll("link[rel=stylesheet]"); - for (let i = 0; i < stylesheets.length; i++) { let sheet = stylesheets[i] as HTMLLinkElement; - - if (forceKicked) { - sheet.setAttribute("data-dioxus-kick", "true"); - } - - // templates won't have the kick attribute, meaning only they need to be kicked - // This lets us skip templates that haven't changed - if (!sheet.hasAttribute("data-dioxus-kick") || forceKicked) { + // Using `cache: reload` will force the browser to re-fetch the stylesheet and bust the cache + fetch(sheet.href, { cache: "reload" }).then(() => { sheet.href = sheet.href + "?" + Math.random(); - } + }); } } From e7a42cc6fcd031ef08b820bfd554a122b7084d7f Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 20 Mar 2024 06:38:23 -0700 Subject: [PATCH 5/7] Less flash, remove log on web hotreload --- packages/web/src/hot_reload.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/web/src/hot_reload.rs b/packages/web/src/hot_reload.rs index 9906ca9712..84024ed068 100644 --- a/packages/web/src/hot_reload.rs +++ b/packages/web/src/hot_reload.rs @@ -50,14 +50,12 @@ pub(crate) fn init() -> UnboundedReceiver