wip: livehost bones

This commit is contained in:
Jonathan Kelley 2021-03-16 11:03:59 -04:00
parent 5b7887d76c
commit 7856f2b153
17 changed files with 154 additions and 117 deletions

View file

@ -5,6 +5,7 @@ members = [
"packages/core-macro",
"packages/core",
"packages/web",
"packages/webview/client"
# "packages/router",
# "packages/ssr",
# "packages/webview",
@ -28,3 +29,5 @@ members = [
# "packages/virtual-dom-rs",
# "packages/virtual-node",
]

View file

@ -5,7 +5,7 @@
</p>
</div>
# About
<!-- # About -->
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.

9
js-host/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "dixous-jshost"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

7
js-host/src/lib.rs Normal file
View file

@ -0,0 +1,7 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View file

@ -306,7 +306,6 @@ unnecessary function runs
Does any of this matter?
Should we just run any component we see, immediately and imperatively? That will cause checks throughout the whole tree, no matter where the update occurred
https://calendar.perfplanet.com/2013/diff/
Here's how react does it:

View file

@ -259,11 +259,11 @@ impl<'a> DiffMachine<'a> {
let id = get_id();
*component.stable_addr.as_ref().borrow_mut() = Some(id);
self.change_list.save_known_root(id);
let scope = Rc::downgrade(&component.ass_scope);
let scope = Rc::downgrade(&component.ass_scope);
self.lifecycle_events.push_back(LifeCycleEvent::Mount {
caller: Rc::downgrade(&component.caller),
id,
scope
scope,
});
}
VNode::Suspended => {
@ -286,20 +286,26 @@ impl<'a> DiffMachine<'a> {
}
'outer1: for (_l_idx, new_l) in new.iter().enumerate() {
// unsafe {
// Safety relies on removing `new_l` from the registry manually at
// the end of its lifetime. This happens below in the `'outer2`
// loop, and elsewhere in diffing when removing old dom trees.
// registry.add(new_l);
// }
// go through each new listener
// find its corresponding partner in the old list
// if any characteristics changed, remove and then re-add
// if nothing changed, then just move on
let event_type = new_l.event;
for old_l in old {
if new_l.event == old_l.event {
if new_l.id != old_l.id {
self.change_list.remove_event_listener(event_type);
self.change_list
.update_event_listener(event_type, new_l.scope, new_l.id)
}
// if let Some(scope) = self.current_idx {
// let cb = CbIdx::from_gi_index(scope, l_idx);
self.change_list
.update_event_listener(event_type, new_l.scope, new_l.id);
// self.change_list
// .update_event_listener(event_type, new_l.scope, new_l.id);
// }
continue 'outer1;

View file

@ -83,7 +83,8 @@ impl Scope {
/// This function downcasts the function pointer based on the stored props_type
///
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
pub fn run_scope<'bump>(&'bump mut self) -> Result<()> {
pub fn run_scope(&mut self) -> Result<()> {
// pub fn run_scope<'bump>(&'bump mut self) -> Result<()> {
let frame = {
let frame = self.frames.next();
frame.bump.reset();
@ -92,7 +93,7 @@ impl Scope {
let node_slot = std::rc::Rc::new(RefCell::new(None));
let ctx: Context<'bump> = Context {
let ctx = Context {
arena: &self.hook_arena,
hooks: &self.hooks,
bump: &frame.bump,
@ -107,7 +108,6 @@ impl Scope {
// todo!()
// Note that the actual modification of the vnode head element occurs during this call
let _: DomTree = (caller.as_ref())(ctx);
// let _: DomTree = (self.raw_caller)(ctx, &self.props);
/*
SAFETY ALERT
@ -123,7 +123,6 @@ impl Scope {
frame.head_node = node_slot
.as_ref()
// .deref()
.borrow_mut()
.take()
.expect("Viewing did not happen");

View file

@ -71,28 +71,16 @@ impl VirtualDom {
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom.
pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
// Diff from the top
let mut diff_machine = DiffMachine::new(); // partial borrow
let mut diff_machine = DiffMachine::new();
let very_unsafe_components = &mut self.components as *mut generational_arena::Arena<Scope>;
let mut component = self
.components
.get_mut(self.base_scope)
.ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?;
component.run_scope()?;
// let component = &*component;
diff_machine.diff_node(component.old_frame(), component.new_frame());
// get raw pointer to the arena
let very_unsafe_components = &mut self.components as *mut generational_arena::Arena<Scope>;
{
let component = self
.components
.get(self.base_scope)
.ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?;
diff_machine.diff_node(component.old_frame(), component.new_frame());
}
// let p = &mut self.components;
// chew down the the lifecycle events until all dirty nodes are computed
while let Some(event) = diff_machine.lifecycle_events.pop_front() {
match event {
@ -115,27 +103,19 @@ impl VirtualDom {
let real_scope = scope.upgrade().unwrap();
*real_scope.as_ref().borrow_mut() = Some(idx);
c.run_scope()?;
diff_machine.change_list.load_known_root(id);
diff_machine.diff_node(c.old_frame(), c.new_frame());
// diff_machine.change_list.save_known_root(id);
}
}
LifeCycleEvent::PropsChanged { caller, id, scope } => {
let idx = scope.upgrade().unwrap().as_ref().borrow().unwrap();
unsafe {
let p = &mut *(very_unsafe_components);
// todo, hook up the parent/child indexes properly
// let idx = p.insert_with(|f| Scope::new(caller, f, None));
let c = p.get_mut(idx).unwrap();
c.update_caller(caller);
c.run_scope()?;
// c.caller = caller;
diff_machine.change_list.load_known_root(id);
diff_machine.diff_node(c.old_frame(), c.new_frame());
}
// break 'render;

View file

@ -6,4 +6,5 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dependencies]
dioxus-core = { path = "../core", version = "0.1.2" }

View file

@ -0,0 +1,14 @@
# Livehost
Stream events from a server to a client.
This crate provides drivers for Actix, Warp, and Tide to run coupled frontend and backend.
This comes in the form of two approaches:
- tight coupling: frontend and backend are locked together
- loose coupling: hooks subscribe a component to a server using the suspense mechanism
Tight coupling is basically an implmentation of loose coupling where **all** events move through the backend connection. This coupling option has higher latency but is very simple to deploy. We use this approach for dioxus-webview where latency is minimal (hosted locally) and we want builds to be simple - no need to manually bundle a custom frontend because everything is server rendered.

View file

@ -1,3 +1,4 @@
<!-- a js-only interpreter for the dioxus patch stream :) -->
<!DOCTYPE html>
<html>
<head>

View file

@ -8,7 +8,7 @@ fn main() {
console_error_panic_hook::set_once();
wasm_bindgen_futures::spawn_local(async {
let props = ExampleProps { initial_name: "..?", blarg: vec!["abc".to_string(), "abc".to_string()]};
let props = ExampleProps { initial_name: "..?"};
WebsysRenderer::new_with_props(Example, props)
.run()
.await
@ -19,13 +19,10 @@ fn main() {
#[derive(PartialEq, Props)]
struct ExampleProps {
initial_name: &'static str,
blarg: Vec<String>
}
static Example: FC<ExampleProps> = |ctx, props| {
let (name, set_name) = use_state(&ctx, move || props.initial_name);
let sub = props.blarg.last().unwrap();
let (name, set_name) = use_state(&ctx, move || props.initial_name.to_string());
ctx.render(rsx! {
div {
@ -39,83 +36,33 @@ static Example: FC<ExampleProps> = |ctx, props| {
"Hello, {name}"
}
// CustomButton { name: sub, set_name: Box::new(move || set_name("jack")) }
CustomButton { name: "Jack!", set_name: Box::new(set_name) }
CustomButton { name: "Jill!", set_name: Box::new(set_name) }
CustomButton { name: "Bob!", set_name: Box::new(set_name) }
CustomButton { name: "Bill!", set_name: Box::new(set_name) }
CustomButton { name: "Dill!", set_name: Box::new(set_name) }
CustomButton { name: "Crack!", set_name: Box::new(set_name) }
CustomButton { name: "back!", set_name: Box::new(set_name) }
CustomButton { name: "cheder!", set_name: Box::new(set_name) }
CustomButton { name: "Jack!", set_name: Box::new(set_name) }
CustomButton { name: "Jill!", set_name: Box::new(set_name) }
CustomButton { name: "Bob!", set_name: Box::new(set_name) }
CustomButton { name: "Bill!", set_name: Box::new(set_name) }
CustomButton { name: "Dill!", set_name: Box::new(set_name) }
CustomButton { name: "Crack!", set_name: Box::new(set_name) }
CustomButton { name: "back!", set_name: Box::new(set_name) }
CustomButton { name: "cheder!", set_name: Box::new(set_name) }
CustomButton { name: "Jack!", set_name: Box::new(set_name) }
CustomButton { name: "Jill!", set_name: Box::new(set_name) }
CustomButton { name: "Bob!", set_name: Box::new(set_name) }
CustomButton { name: "Bill!", set_name: Box::new(set_name) }
CustomButton { name: "Dill!", set_name: Box::new(set_name) }
CustomButton { name: "Crack!", set_name: Box::new(set_name) }
CustomButton { name: "back!", set_name: Box::new(set_name) }
CustomButton { name: "cheder!", set_name: Box::new(set_name) }
CustomButton { name: "Jack!", set_name: Box::new(set_name) }
CustomButton { name: "Jill!", set_name: Box::new(set_name) }
CustomButton { name: "Bob!", set_name: Box::new(set_name) }
CustomButton { name: "Bill!", set_name: Box::new(set_name) }
CustomButton { name: "Dill!", set_name: Box::new(set_name) }
CustomButton { name: "Crack!", set_name: Box::new(set_name) }
CustomButton { name: "back!", set_name: Box::new(set_name) }
CustomButton { name: "cheder!", set_name: Box::new(set_name) }
CustomButton { name: "Jack!", set_name: Box::new(set_name) }
CustomButton { name: "Jill!", set_name: Box::new(set_name) }
CustomButton { name: "Bob!", set_name: Box::new(set_name) }
CustomButton { name: "Bill!", set_name: Box::new(set_name) }
CustomButton { name: "Dill!", set_name: Box::new(set_name) }
CustomButton { name: "Crack!", set_name: Box::new(set_name) }
CustomButton { name: "back!", set_name: Box::new(set_name) }
CustomButton { name: "cheder!", set_name: Box::new(set_name) }
CustomButton { name: "Jack!", set_name: Box::new(set_name) }
CustomButton { name: "Jill!", set_name: Box::new(set_name) }
CustomButton { name: "Bob!", set_name: Box::new(set_name) }
CustomButton { name: "Bill!", set_name: Box::new(set_name) }
CustomButton { name: "Dill!", set_name: Box::new(set_name) }
CustomButton { name: "Crack!", set_name: Box::new(set_name) }
CustomButton { name: "back!", set_name: Box::new(set_name) }
CustomButton { name: "cheder!", set_name: Box::new(set_name) }
// CustomButton { name: "Bill!", set_name: Box::new(move || set_name("Bill")) }
// CustomButton { name: "Reset!", set_name: Box::new(move || set_name(props.initial_name)) }
CustomButton { name: "Jack!", set_name: set_name }
CustomButton { name: "Jill!", set_name: set_name }
CustomButton { name: "Bob!", set_name: set_name }
}
})
};
#[derive(Props)]
struct ButtonProps<'src> {
name: &'static str,
// name: &'src str,
set_name: Box< dyn Fn(&'static str) + 'src>
name: &'src str,
set_name: &'src dyn Fn(String)
}
/// this is an awesome component
fn CustomButton<'a>(ctx: Context<'a>, props: &'a ButtonProps<'a>) -> DomTree {
ctx.render(rsx!{
button {
class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
onmouseover: move |evt| (props.set_name)(props.name.to_string())
"{props.name}"
}
})
}
impl PartialEq for ButtonProps<'_> {
fn eq(&self, other: &Self) -> bool {
false
}
}
fn CustomButton<'a>(ctx: Context<'a>, props: &'a ButtonProps<'a>) -> DomTree {
ctx.render(rsx!{
// div {
button {
class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
onmouseover: move |_| (props.set_name)(props.name)
"{props.name}"
}
// }
})
}

View file

@ -21,4 +21,5 @@ thiserror = "1.0.23"
log = "0.4.13"
fern = { version = "0.6.0", features = ["colored"] }
[build-dependencies]

View file

@ -36,13 +36,12 @@ dioxus bundle --platform macOS
## Goals
Because the host VirtualDOM is running in its own native process, native applications can unlock their full potential. Dioxus-webview is designed to be a 100% rust alternative to ElectronJS without the memory overhead or bloat of ElectronJS apps.
Because the host VirtualDOM is running in its own native process, native applications can unlock their full potential. Dioxus-webview is designed to be a 100% rust alternative to ElectronJS without the memory overhead or bloat of ElectronJS apps.
By bridging the native process, desktop apps can access full multithreading power, peripheral support, hardware access, and native filesystem controls without the hassle of web technologies. Our goal with Dioxus-webview is to make it easy to ship both a web and native application, and quickly see large performance boosts without having to re-write the whole stack. As the dioxus ecosystem grows, we hope to see 3rd parties providing wrappers for storage, offline mode, etc that supports both web and native technologies.
## Tech
Dioxus-desktop is a pure liveview application where all of the state and event handlers are proxied through the liveview and into the native process. For pure server-based liveview, this would normally be too slow (in both render performance and latency), but because the VDom is local, desktop apps are just as fast as Electron.
Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime is needed to
Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime *is* needed - we recommend async-std in tokio mode.

View file

@ -0,0 +1,10 @@
[package]
name = "webview-client"
version = "0.1.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus-core = {path = "../../core"}

View file

@ -0,0 +1,61 @@
use dioxus_core::patch::Edit;
use dioxus_core::prelude::*;
static SERVER_RENDERED_KEY: &'static str = "abc123";
#[derive(Debug, PartialEq, Props)]
struct ServerRendered {
name0: String,
name1: String,
name2: String,
name3: String,
name4: String,
name5: String,
name6: String,
name7: String,
}
fn main() {
// connect to the host server
let server_rendered = use_server_rendered((111111, 11111, 11), SERVER_RENDERED_KEY, || {
ServerRendered {
name0: "abc".to_string(),
name1: "abc".to_string(),
name2: "abc".to_string(),
name3: "abc".to_string(),
name4: "abc".to_string(),
name5: "abc".to_string(),
name6: "abc".to_string(),
name7: "abc".to_string(),
}
});
let handler = dioxus_liveview::new_handler()
.from_directory("abc123") // serve a given directory as the root
.with_context(|| SomeContext {}) // build out a new context for all of the server-rendered components to share
.with_route(SERVER_RENDERED_KEY, |ctx, props: &ServerRendered| {
//
})
.with_route(SERVER_RENDERED_KEY, |ctx, props| {
//
})
.with_route(SERVER_RENDERED_KEY, |ctx, props| {
//
})
.with_route(SERVER_RENDERED_KEY, |ctx, props| {
//
})
.with_route(SERVER_RENDERED_KEY, |ctx, props| {
//
})
.with_route(SERVER_RENDERED_KEY, |ctx, props| {
//
})
// depend on the framework, build a different type of handler
// there are instructions on how to integrate this the various rusty web frameworks in the guide
.build();
server.get("abc", handler);
}
fn use_server_rendered<F: Properties>(_p: impl PartialEq, name: &'static str, f: impl Fn() -> F) {}