···4# paths because the flake.nix is written in a way such that top-level members
5# (`weaver-cli` and `weaver-server`) are built as different derivations which avoid being
6# rebuilt if the other package's sources change.
7-members = ["crates/*"]
89#default-members = ["crates/weaver-cli"]
10···43jacquard-api = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
44jacquard-axum = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
45tree_magic = { version = "0.2.3", features = ["cli"] }
000000000000
···4# paths because the flake.nix is written in a way such that top-level members
5# (`weaver-cli` and `weaver-server`) are built as different derivations which avoid being
6# rebuilt if the other package's sources change.
7+members = ["crates/*", "crates/weaver-server"]
89#default-members = ["crates/weaver-cli"]
10···43jacquard-api = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
44jacquard-axum = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
45tree_magic = { version = "0.2.3", features = ["cli"] }
46+47+[profile]
48+49+[profile.wasm-dev]
50+inherits = "dev"
51+opt-level = 1
52+53+[profile.server-dev]
54+inherits = "dev"
55+56+[profile.android-dev]
57+inherits = "dev"
···1+# Generated by Cargo
2+# will have compiled files and executables
3+/target
4+.DS_Store
5+6+# These are backup files generated by rustfmt
7+**/*.rs.bk
···1[package]
2name = "weaver-server"
3+version = "0.1.0"
4+authors = ["Orual <orual@nonbinary.computer>"]
5+edition = "2021"
067+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
00000089[dependencies]
10+dioxus = { version = "0.6.0", features = ["router", "fullstack"] }
11weaver-common = { path = "../weaver-common" }
12weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" }
1314jacquard = { workspace = true, features = ["tracing"] }
01516+[features]
17+default = ["web"]
18+# The feature that are only required for the web = ["dioxus/web"] build target should be optional and only enabled in the web = ["dioxus/web"] feature
19+web = ["dioxus/web"]
20+# The feature that are only required for the desktop = ["dioxus/desktop"] build target should be optional and only enabled in the desktop = ["dioxus/desktop"] feature
21+desktop = ["dioxus/desktop"]
22+# The feature that are only required for the mobile = ["dioxus/mobile"] build target should be optional and only enabled in the mobile = ["dioxus/mobile"] feature
23+mobile = ["dioxus/mobile"]
+21
crates/weaver-server/Dioxus.toml
···000000000000000000000
···1+[application]
2+3+[web.app]
4+5+# HTML title tag content
6+title = "weaver-server"
7+8+# include `assets` in web platform
9+[web.resource]
10+11+# Additional CSS style files
12+style = []
13+14+# Additional JavaScript files
15+script = []
16+17+[web.resource.dev]
18+19+# Javascript code file
20+# serve: [dev-server] only
21+script = []
+34
crates/weaver-server/README.md
···0000000000000000000000000000000000
···1+# Development
2+3+Your new jumpstart project includes basic organization with an organized `assets` folder and a `components` folder.
4+If you chose to develop with the router feature, you will also have a `views` folder.
5+6+```
7+project/
8+├─ assets/ # Any assets that are used by the app should be placed here
9+├─ src/
10+│ ├─ main.rs # The entrypoint for the app. It also defines the routes for the app.
11+│ ├─ components/
12+│ │ ├─ mod.rs # Defines the components module
13+│ │ ├─ hero.rs # The Hero component for use in the home page
14+│ │ ├─ echo.rs # The echo component uses server functions to communicate with the server
15+│ ├─ views/ # The views each route will render in the app.
16+│ │ ├─ mod.rs # Defines the module for the views route and re-exports the components for each route
17+│ │ ├─ blog.rs # The component that will render at the /blog/:id route
18+│ │ ├─ home.rs # The component that will render at the / route
19+├─ Cargo.toml # The Cargo.toml file defines the dependencies and feature flags for your project
20+```
21+22+### Serving Your App
23+24+Run the following command in the root of your project to start developing with the default platform:
25+26+```bash
27+dx serve --platform web
28+```
29+30+To run for a different platform, use the `--platform platform` flag. E.g.
31+```bash
32+dx serve --platform desktop
33+```
34+
···1+await-holding-invalid-types = [
2+ "generational_box::GenerationalRef",
3+ { path = "generational_box::GenerationalRef", reason = "Reads should not be held over an await point. This will cause any writes to fail while the await is pending since the read borrow is still active." },
4+ "generational_box::GenerationalRefMut",
5+ { path = "generational_box::GenerationalRefMut", reason = "Write should not be held over an await point. This will cause any reads or writes to fail while the await is pending since the write borrow is still active." },
6+ "dioxus_signals::Write",
7+ { path = "dioxus_signals::Write", reason = "Write should not be held over an await point. This will cause any reads or writes to fail while the await is pending since the write borrow is still active." },
8+]
···1+use dioxus::prelude::*;
2+3+const ECHO_CSS: Asset = asset!("/assets/styling/echo.css");
4+5+/// Echo component that demonstrates fullstack server functions.
6+#[component]
7+pub fn Echo() -> Element {
8+ // use_signal is a hook. Hooks in dioxus must be run in a consistent order every time the component is rendered.
9+ // That means they can't be run inside other hooks, async blocks, if statements, or loops.
10+ //
11+ // use_signal is a hook that creates a state for the component. It takes a closure that returns the initial value of the state.
12+ // The state is automatically tracked and will rerun any other hooks or components that read it whenever it changes.
13+ let mut response = use_signal(|| String::new());
14+15+ rsx! {
16+ document::Link { rel: "stylesheet", href: ECHO_CSS }
17+18+ div {
19+ id: "echo",
20+ h4 { "ServerFn Echo" }
21+ input {
22+ placeholder: "Type here to echo...",
23+ // `oninput` is an event handler that will run when the input changes. It can return either nothing or a future
24+ // that will be run when the event runs.
25+ oninput: move |event| async move {
26+ // When we call the echo_server function from the client, it will fire a request to the server and return
27+ // the response. It handles serialization and deserialization of the request and response for us.
28+ let data = echo_server(event.value()).await.unwrap();
29+30+ // After we have the data from the server, we can set the state of the signal to the new value.
31+ // Since we read the `response` signal later in this component, the component will rerun.
32+ response.set(data);
33+ },
34+ }
35+36+ // Signals can be called like a function to clone the current value of the signal
37+ if !response().is_empty() {
38+ p {
39+ "Server echoed: "
40+ // Since we read the signal inside this component, the component "subscribes" to the signal. Whenever
41+ // the signal changes, the component will rerun.
42+ i { "{response}" }
43+ }
44+ }
45+ }
46+ }
47+}
48+49+// Server functions let us define public APIs on the server that can be called like a normal async function from the client.
50+// Each server function needs to be annotated with the `#[server]` attribute, accept and return serializable types, and return
51+// a `Result` with the error type [`ServerFnError`].
52+//
53+// When the server function is called from the client, it will just serialize the arguments, call the API, and deserialize the
54+// response.
55+#[server]
56+async fn echo_server(input: String) -> Result<String, ServerFnError> {
57+ // The body of server function like this comment are only included on the server. If you have any server-only logic like
58+ // database queries, you can put it here. Any imports for the server function should either be imported inside the function
59+ // or imported under a `#[cfg(feature = "server")]` block.
60+ Ok(input)
61+}
+25
crates/weaver-server/src/components/hero.rs
···0000000000000000000000000
···1+use dioxus::prelude::*;
2+3+const HEADER_SVG: Asset = asset!("/assets/header.svg");
4+5+#[component]
6+pub fn Hero() -> Element {
7+ rsx! {
8+ // We can create elements inside the rsx macro with the element name followed by a block of attributes and children.
9+ div {
10+ // Attributes should be defined in the element before any children
11+ id: "hero",
12+ // After all attributes are defined, we can define child elements and components
13+ img { src: HEADER_SVG, id: "header" }
14+ div { id: "links",
15+ // The RSX macro also supports text nodes surrounded by quotes
16+ a { href: "https://dioxuslabs.com/learn/0.6/", "📚 Learn Dioxus" }
17+ a { href: "https://dioxuslabs.com/awesome", "🚀 Awesome Dioxus" }
18+ a { href: "https://github.com/dioxus-community/", "📡 Community Libraries" }
19+ a { href: "https://github.com/DioxusLabs/sdk", "⚙️ Dioxus Development Kit" }
20+ a { href: "https://marketplace.visualstudio.com/items?itemName=DioxusLabs.dioxus", "💫 VSCode Extension" }
21+ a { href: "https://discord.gg/XgGxMSkvUM", "👋 Community Discord" }
22+ }
23+ }
24+ }
25+}
+9
crates/weaver-server/src/components/mod.rs
···000000000
···1+//! The components module contains all shared components for our app. Components are the building blocks of dioxus apps.
2+//! They can be used to defined common UI elements like buttons, forms, and modals. In this template, we define a Hero
3+//! component and an Echo component for fullstack apps to be used in our app.
4+5+mod hero;
6+pub use hero::Hero;
7+8+mod echo;
9+pub use echo::Echo;
···1-//! Weaver server
2-//!
3-//! This crate is a lightweight HTTP server which can serve a notebook.
4-//! It will auto-reload
56-use axum::{Router, response::Html, routing::get};
78-use tokio::net::TcpListener;
000910-#[tokio::main]
11-async fn main() {}
000000000000000000001213-async fn handler() -> Html<&'static str> {
14- Html("<h1>Hello, World!</h1>")
000000000000000000000000000015}
···1+// The dioxus prelude contains a ton of common items used in dioxus apps. It's a good idea to import wherever you
2+// need dioxus
3+use dioxus::prelude::*;
045+use views::{Blog, Home, Navbar};
67+/// Define a components module that contains all shared components for our app.
8+mod components;
9+/// Define a views module that contains the UI for all Layouts and Routes for our app.
10+mod views;
1112+/// The Route enum is used to define the structure of internal routes in our app. All route enums need to derive
13+/// the [`Routable`] trait, which provides the necessary methods for the router to work.
14+///
15+/// Each variant represents a different URL pattern that can be matched by the router. If that pattern is matched,
16+/// the components for that route will be rendered.
17+#[derive(Debug, Clone, Routable, PartialEq)]
18+#[rustfmt::skip]
19+enum Route {
20+ // The layout attribute defines a wrapper for all routes under the layout. Layouts are great for wrapping
21+ // many routes with a common UI like a navbar.
22+ #[layout(Navbar)]
23+ // The route attribute defines the URL pattern that a specific route matches. If that pattern matches the URL,
24+ // the component for that route will be rendered. The component name that is rendered defaults to the variant name.
25+ #[route("/")]
26+ Home {},
27+ // The route attribute can include dynamic parameters that implement [`std::str::FromStr`] and [`std::fmt::Display`] with the `:` syntax.
28+ // In this case, id will match any integer like `/blog/123` or `/blog/-456`.
29+ #[route("/blog/:id")]
30+ // Fields of the route variant will be passed to the component as props. In this case, the blog component must accept
31+ // an `id` prop of type `i32`.
32+ Blog { id: i32 },
33+}
3435+// We can import assets in dioxus with the `asset!` macro. This macro takes a path to an asset relative to the crate root.
36+// The macro returns an `Asset` type that will display as the path to the asset in the browser or a local path in desktop bundles.
37+const FAVICON: Asset = asset!("/assets/favicon.ico");
38+// The asset macro also minifies some assets like CSS and JS to make bundled smaller
39+const MAIN_CSS: Asset = asset!("/assets/styling/main.css");
40+41+fn main() {
42+ // The `launch` function is the main entry point for a dioxus app. It takes a component and renders it with the platform feature
43+ // you have enabled
44+ dioxus::launch(App);
45+}
46+47+/// App is the main component of our app. Components are the building blocks of dioxus apps. Each component is a function
48+/// that takes some props and returns an Element. In this case, App takes no props because it is the root of our app.
49+///
50+/// Components should be annotated with `#[component]` to support props, better error messages, and autocomplete
51+#[component]
52+fn App() -> Element {
53+ // The `rsx!` macro lets us define HTML inside of rust. It expands to an Element with all of our HTML inside.
54+ rsx! {
55+ // In addition to element and text (which we will see later), rsx can contain other components. In this case,
56+ // we are using the `document::Link` component to add a link to our favicon and main CSS file into the head of our app.
57+ document::Link { rel: "icon", href: FAVICON }
58+ document::Link { rel: "stylesheet", href: MAIN_CSS }
59+60+61+ // The router component renders the route enum we defined above. It will handle synchronization of the URL and render
62+ // the layouts and components for the active route.
63+ Router::<Route> {}
64+ }
65}
+39
crates/weaver-server/src/views/blog.rs
···000000000000000000000000000000000000000
···1+use crate::Route;
2+use dioxus::prelude::*;
3+4+const BLOG_CSS: Asset = asset!("/assets/styling/blog.css");
5+6+/// The Blog page component that will be rendered when the current route is `[Route::Blog]`
7+///
8+/// The component takes a `id` prop of type `i32` from the route enum. Whenever the id changes, the component function will be
9+/// re-run and the rendered HTML will be updated.
10+#[component]
11+pub fn Blog(id: i32) -> Element {
12+ rsx! {
13+ document::Link { rel: "stylesheet", href: BLOG_CSS }
14+15+ div {
16+ id: "blog",
17+18+ // Content
19+ h1 { "This is blog #{id}!" }
20+ p { "In blog #{id}, we show how the Dioxus router works and how URL parameters can be passed as props to our route components." }
21+22+ // Navigation links
23+ // The `Link` component lets us link to other routes inside our app. It takes a `to` prop of type `Route` and
24+ // any number of child nodes.
25+ Link {
26+ // The `to` prop is the route that the link should navigate to. We can use the `Route` enum to link to the
27+ // blog page with the id of -1. Since we are using an enum instead of a string, all of the routes will be checked
28+ // at compile time to make sure they are valid.
29+ to: Route::Blog { id: id - 1 },
30+ "Previous"
31+ }
32+ span { " <---> " }
33+ Link {
34+ to: Route::Blog { id: id + 1 },
35+ "Next"
36+ }
37+ }
38+ }
39+}
+11
crates/weaver-server/src/views/home.rs
···00000000000
···1+use crate::components::{Echo, Hero};
2+use dioxus::prelude::*;
3+4+/// The Home page component that will be rendered when the current route is `[Route::Home]`
5+#[component]
6+pub fn Home() -> Element {
7+ rsx! {
8+ Hero {}
9+ Echo {}
10+ }
11+}
+18
crates/weaver-server/src/views/mod.rs
···000000000000000000
···1+//! The views module contains the components for all Layouts and Routes for our app. Each layout and route in our [`Route`]
2+//! enum will render one of these components.
3+//!
4+//!
5+//! The [`Home`] and [`Blog`] components will be rendered when the current route is [`Route::Home`] or [`Route::Blog`] respectively.
6+//!
7+//!
8+//! The [`Navbar`] component will be rendered on all pages of our app since every page is under the layout. The layout defines
9+//! a common wrapper around all child routes.
10+11+mod home;
12+pub use home::Home;
13+14+mod blog;
15+pub use blog::Blog;
16+17+mod navbar;
18+pub use navbar::Navbar;
+32
crates/weaver-server/src/views/navbar.rs
···00000000000000000000000000000000
···1+use crate::Route;
2+use dioxus::prelude::*;
3+4+const NAVBAR_CSS: Asset = asset!("/assets/styling/navbar.css");
5+6+/// The Navbar component that will be rendered on all pages of our app since every page is under the layout.
7+///
8+///
9+/// This layout component wraps the UI of [Route::Home] and [Route::Blog] in a common navbar. The contents of the Home and Blog
10+/// routes will be rendered under the outlet inside this component
11+#[component]
12+pub fn Navbar() -> Element {
13+ rsx! {
14+ document::Link { rel: "stylesheet", href: NAVBAR_CSS }
15+16+ div {
17+ id: "navbar",
18+ Link {
19+ to: Route::Home {},
20+ "Home"
21+ }
22+ Link {
23+ to: Route::Blog { id: 1 },
24+ "Blog"
25+ }
26+ }
27+28+ // The `Outlet` component is used to render the next component inside the layout. In this case, it will render either
29+ // the [`Home`] or [`Blog`] component depending on the current route.
30+ Outlet::<Route> {}
31+ }
32+}