···44# paths because the flake.nix is written in a way such that top-level members
55# (`weaver-cli` and `weaver-server`) are built as different derivations which avoid being
66# rebuilt if the other package's sources change.
77-members = ["crates/*"]
77+members = ["crates/*", "crates/weaver-server"]
8899#default-members = ["crates/weaver-cli"]
1010···4343jacquard-api = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
4444jacquard-axum = { git = "https://tangled.org/@nonbinary.computer/jacquard" }
4545tree_magic = { version = "0.2.3", features = ["cli"] }
4646+4747+[profile]
4848+4949+[profile.wasm-dev]
5050+inherits = "dev"
5151+opt-level = 1
5252+5353+[profile.server-dev]
5454+inherits = "dev"
5555+5656+[profile.android-dev]
5757+inherits = "dev"
···11+# Generated by Cargo
22+# will have compiled files and executables
33+/target
44+.DS_Store
55+66+# These are backup files generated by rustfmt
77+**/*.rs.bk
+13-17
crates/weaver-server/Cargo.toml
···11[package]
22name = "weaver-server"
33-version.workspace = true
44-edition.workspace = true
55-license.workspace = true
66-publish = false
33+version = "0.1.0"
44+authors = ["Orual <orual@nonbinary.computer>"]
55+edition = "2021"
7688-[[bin]]
99-name = "weaver-server"
1010-path = "src/main.rs"
1111-1212-[lib]
1313-name = "weaver_server"
1414-path = "src/lib.rs"
77+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
158169[dependencies]
1010+dioxus = { version = "0.6.0", features = ["router", "fullstack"] }
1711weaver-common = { path = "../weaver-common" }
1812weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" }
19132014jacquard = { workspace = true, features = ["tracing"] }
2121-jacquard-axum = { workspace = true }
22152323-axum = "0.8"
2424-tokio = { version = "1.44", features = ["full"] }
2525-notify = "8.0"
2626-2727-clap = { version = "4.5", features = ["derive", "env", "cargo", "unicode"] }
1616+[features]
1717+default = ["web"]
1818+# 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
1919+web = ["dioxus/web"]
2020+# 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
2121+desktop = ["dioxus/desktop"]
2222+# 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
2323+mobile = ["dioxus/mobile"]
+21
crates/weaver-server/Dioxus.toml
···11+[application]
22+33+[web.app]
44+55+# HTML title tag content
66+title = "weaver-server"
77+88+# include `assets` in web platform
99+[web.resource]
1010+1111+# Additional CSS style files
1212+style = []
1313+1414+# Additional JavaScript files
1515+script = []
1616+1717+[web.resource.dev]
1818+1919+# Javascript code file
2020+# serve: [dev-server] only
2121+script = []
+34
crates/weaver-server/README.md
···11+# Development
22+33+Your new jumpstart project includes basic organization with an organized `assets` folder and a `components` folder.
44+If you chose to develop with the router feature, you will also have a `views` folder.
55+66+```
77+project/
88+├─ assets/ # Any assets that are used by the app should be placed here
99+├─ src/
1010+│ ├─ main.rs # The entrypoint for the app. It also defines the routes for the app.
1111+│ ├─ components/
1212+│ │ ├─ mod.rs # Defines the components module
1313+│ │ ├─ hero.rs # The Hero component for use in the home page
1414+│ │ ├─ echo.rs # The echo component uses server functions to communicate with the server
1515+│ ├─ views/ # The views each route will render in the app.
1616+│ │ ├─ mod.rs # Defines the module for the views route and re-exports the components for each route
1717+│ │ ├─ blog.rs # The component that will render at the /blog/:id route
1818+│ │ ├─ home.rs # The component that will render at the / route
1919+├─ Cargo.toml # The Cargo.toml file defines the dependencies and feature flags for your project
2020+```
2121+2222+### Serving Your App
2323+2424+Run the following command in the root of your project to start developing with the default platform:
2525+2626+```bash
2727+dx serve --platform web
2828+```
2929+3030+To run for a different platform, use the `--platform platform` flag. E.g.
3131+```bash
3232+dx serve --platform desktop
3333+```
3434+
···11+await-holding-invalid-types = [
22+ "generational_box::GenerationalRef",
33+ { 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." },
44+ "generational_box::GenerationalRefMut",
55+ { 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." },
66+ "dioxus_signals::Write",
77+ { 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." },
88+]
+61
crates/weaver-server/src/components/echo.rs
···11+use dioxus::prelude::*;
22+33+const ECHO_CSS: Asset = asset!("/assets/styling/echo.css");
44+55+/// Echo component that demonstrates fullstack server functions.
66+#[component]
77+pub fn Echo() -> Element {
88+ // use_signal is a hook. Hooks in dioxus must be run in a consistent order every time the component is rendered.
99+ // That means they can't be run inside other hooks, async blocks, if statements, or loops.
1010+ //
1111+ // use_signal is a hook that creates a state for the component. It takes a closure that returns the initial value of the state.
1212+ // The state is automatically tracked and will rerun any other hooks or components that read it whenever it changes.
1313+ let mut response = use_signal(|| String::new());
1414+1515+ rsx! {
1616+ document::Link { rel: "stylesheet", href: ECHO_CSS }
1717+1818+ div {
1919+ id: "echo",
2020+ h4 { "ServerFn Echo" }
2121+ input {
2222+ placeholder: "Type here to echo...",
2323+ // `oninput` is an event handler that will run when the input changes. It can return either nothing or a future
2424+ // that will be run when the event runs.
2525+ oninput: move |event| async move {
2626+ // When we call the echo_server function from the client, it will fire a request to the server and return
2727+ // the response. It handles serialization and deserialization of the request and response for us.
2828+ let data = echo_server(event.value()).await.unwrap();
2929+3030+ // After we have the data from the server, we can set the state of the signal to the new value.
3131+ // Since we read the `response` signal later in this component, the component will rerun.
3232+ response.set(data);
3333+ },
3434+ }
3535+3636+ // Signals can be called like a function to clone the current value of the signal
3737+ if !response().is_empty() {
3838+ p {
3939+ "Server echoed: "
4040+ // Since we read the signal inside this component, the component "subscribes" to the signal. Whenever
4141+ // the signal changes, the component will rerun.
4242+ i { "{response}" }
4343+ }
4444+ }
4545+ }
4646+ }
4747+}
4848+4949+// Server functions let us define public APIs on the server that can be called like a normal async function from the client.
5050+// Each server function needs to be annotated with the `#[server]` attribute, accept and return serializable types, and return
5151+// a `Result` with the error type [`ServerFnError`].
5252+//
5353+// When the server function is called from the client, it will just serialize the arguments, call the API, and deserialize the
5454+// response.
5555+#[server]
5656+async fn echo_server(input: String) -> Result<String, ServerFnError> {
5757+ // The body of server function like this comment are only included on the server. If you have any server-only logic like
5858+ // database queries, you can put it here. Any imports for the server function should either be imported inside the function
5959+ // or imported under a `#[cfg(feature = "server")]` block.
6060+ Ok(input)
6161+}
+25
crates/weaver-server/src/components/hero.rs
···11+use dioxus::prelude::*;
22+33+const HEADER_SVG: Asset = asset!("/assets/header.svg");
44+55+#[component]
66+pub fn Hero() -> Element {
77+ rsx! {
88+ // We can create elements inside the rsx macro with the element name followed by a block of attributes and children.
99+ div {
1010+ // Attributes should be defined in the element before any children
1111+ id: "hero",
1212+ // After all attributes are defined, we can define child elements and components
1313+ img { src: HEADER_SVG, id: "header" }
1414+ div { id: "links",
1515+ // The RSX macro also supports text nodes surrounded by quotes
1616+ a { href: "https://dioxuslabs.com/learn/0.6/", "📚 Learn Dioxus" }
1717+ a { href: "https://dioxuslabs.com/awesome", "🚀 Awesome Dioxus" }
1818+ a { href: "https://github.com/dioxus-community/", "📡 Community Libraries" }
1919+ a { href: "https://github.com/DioxusLabs/sdk", "⚙️ Dioxus Development Kit" }
2020+ a { href: "https://marketplace.visualstudio.com/items?itemName=DioxusLabs.dioxus", "💫 VSCode Extension" }
2121+ a { href: "https://discord.gg/XgGxMSkvUM", "👋 Community Discord" }
2222+ }
2323+ }
2424+ }
2525+}
+9
crates/weaver-server/src/components/mod.rs
···11+//! The components module contains all shared components for our app. Components are the building blocks of dioxus apps.
22+//! They can be used to defined common UI elements like buttons, forms, and modals. In this template, we define a Hero
33+//! component and an Echo component for fullstack apps to be used in our app.
44+55+mod hero;
66+pub use hero::Hero;
77+88+mod echo;
99+pub use echo::Echo;
···11-//! Weaver server
22-//!
33-//! This crate is a lightweight HTTP server which can serve a notebook.
44-//! It will auto-reload
11+// The dioxus prelude contains a ton of common items used in dioxus apps. It's a good idea to import wherever you
22+// need dioxus
33+use dioxus::prelude::*;
5466-use axum::{Router, response::Html, routing::get};
55+use views::{Blog, Home, Navbar};
7688-use tokio::net::TcpListener;
77+/// Define a components module that contains all shared components for our app.
88+mod components;
99+/// Define a views module that contains the UI for all Layouts and Routes for our app.
1010+mod views;
9111010-#[tokio::main]
1111-async fn main() {}
1212+/// The Route enum is used to define the structure of internal routes in our app. All route enums need to derive
1313+/// the [`Routable`] trait, which provides the necessary methods for the router to work.
1414+///
1515+/// Each variant represents a different URL pattern that can be matched by the router. If that pattern is matched,
1616+/// the components for that route will be rendered.
1717+#[derive(Debug, Clone, Routable, PartialEq)]
1818+#[rustfmt::skip]
1919+enum Route {
2020+ // The layout attribute defines a wrapper for all routes under the layout. Layouts are great for wrapping
2121+ // many routes with a common UI like a navbar.
2222+ #[layout(Navbar)]
2323+ // The route attribute defines the URL pattern that a specific route matches. If that pattern matches the URL,
2424+ // the component for that route will be rendered. The component name that is rendered defaults to the variant name.
2525+ #[route("/")]
2626+ Home {},
2727+ // The route attribute can include dynamic parameters that implement [`std::str::FromStr`] and [`std::fmt::Display`] with the `:` syntax.
2828+ // In this case, id will match any integer like `/blog/123` or `/blog/-456`.
2929+ #[route("/blog/:id")]
3030+ // Fields of the route variant will be passed to the component as props. In this case, the blog component must accept
3131+ // an `id` prop of type `i32`.
3232+ Blog { id: i32 },
3333+}
12341313-async fn handler() -> Html<&'static str> {
1414- Html("<h1>Hello, World!</h1>")
3535+// We can import assets in dioxus with the `asset!` macro. This macro takes a path to an asset relative to the crate root.
3636+// 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.
3737+const FAVICON: Asset = asset!("/assets/favicon.ico");
3838+// The asset macro also minifies some assets like CSS and JS to make bundled smaller
3939+const MAIN_CSS: Asset = asset!("/assets/styling/main.css");
4040+4141+fn main() {
4242+ // The `launch` function is the main entry point for a dioxus app. It takes a component and renders it with the platform feature
4343+ // you have enabled
4444+ dioxus::launch(App);
4545+}
4646+4747+/// App is the main component of our app. Components are the building blocks of dioxus apps. Each component is a function
4848+/// that takes some props and returns an Element. In this case, App takes no props because it is the root of our app.
4949+///
5050+/// Components should be annotated with `#[component]` to support props, better error messages, and autocomplete
5151+#[component]
5252+fn App() -> Element {
5353+ // The `rsx!` macro lets us define HTML inside of rust. It expands to an Element with all of our HTML inside.
5454+ rsx! {
5555+ // In addition to element and text (which we will see later), rsx can contain other components. In this case,
5656+ // we are using the `document::Link` component to add a link to our favicon and main CSS file into the head of our app.
5757+ document::Link { rel: "icon", href: FAVICON }
5858+ document::Link { rel: "stylesheet", href: MAIN_CSS }
5959+6060+6161+ // The router component renders the route enum we defined above. It will handle synchronization of the URL and render
6262+ // the layouts and components for the active route.
6363+ Router::<Route> {}
6464+ }
1565}
+39
crates/weaver-server/src/views/blog.rs
···11+use crate::Route;
22+use dioxus::prelude::*;
33+44+const BLOG_CSS: Asset = asset!("/assets/styling/blog.css");
55+66+/// The Blog page component that will be rendered when the current route is `[Route::Blog]`
77+///
88+/// The component takes a `id` prop of type `i32` from the route enum. Whenever the id changes, the component function will be
99+/// re-run and the rendered HTML will be updated.
1010+#[component]
1111+pub fn Blog(id: i32) -> Element {
1212+ rsx! {
1313+ document::Link { rel: "stylesheet", href: BLOG_CSS }
1414+1515+ div {
1616+ id: "blog",
1717+1818+ // Content
1919+ h1 { "This is blog #{id}!" }
2020+ p { "In blog #{id}, we show how the Dioxus router works and how URL parameters can be passed as props to our route components." }
2121+2222+ // Navigation links
2323+ // The `Link` component lets us link to other routes inside our app. It takes a `to` prop of type `Route` and
2424+ // any number of child nodes.
2525+ Link {
2626+ // The `to` prop is the route that the link should navigate to. We can use the `Route` enum to link to the
2727+ // blog page with the id of -1. Since we are using an enum instead of a string, all of the routes will be checked
2828+ // at compile time to make sure they are valid.
2929+ to: Route::Blog { id: id - 1 },
3030+ "Previous"
3131+ }
3232+ span { " <---> " }
3333+ Link {
3434+ to: Route::Blog { id: id + 1 },
3535+ "Next"
3636+ }
3737+ }
3838+ }
3939+}
+11
crates/weaver-server/src/views/home.rs
···11+use crate::components::{Echo, Hero};
22+use dioxus::prelude::*;
33+44+/// The Home page component that will be rendered when the current route is `[Route::Home]`
55+#[component]
66+pub fn Home() -> Element {
77+ rsx! {
88+ Hero {}
99+ Echo {}
1010+ }
1111+}
+18
crates/weaver-server/src/views/mod.rs
···11+//! The views module contains the components for all Layouts and Routes for our app. Each layout and route in our [`Route`]
22+//! enum will render one of these components.
33+//!
44+//!
55+//! The [`Home`] and [`Blog`] components will be rendered when the current route is [`Route::Home`] or [`Route::Blog`] respectively.
66+//!
77+//!
88+//! The [`Navbar`] component will be rendered on all pages of our app since every page is under the layout. The layout defines
99+//! a common wrapper around all child routes.
1010+1111+mod home;
1212+pub use home::Home;
1313+1414+mod blog;
1515+pub use blog::Blog;
1616+1717+mod navbar;
1818+pub use navbar::Navbar;
+32
crates/weaver-server/src/views/navbar.rs
···11+use crate::Route;
22+use dioxus::prelude::*;
33+44+const NAVBAR_CSS: Asset = asset!("/assets/styling/navbar.css");
55+66+/// The Navbar component that will be rendered on all pages of our app since every page is under the layout.
77+///
88+///
99+/// This layout component wraps the UI of [Route::Home] and [Route::Blog] in a common navbar. The contents of the Home and Blog
1010+/// routes will be rendered under the outlet inside this component
1111+#[component]
1212+pub fn Navbar() -> Element {
1313+ rsx! {
1414+ document::Link { rel: "stylesheet", href: NAVBAR_CSS }
1515+1616+ div {
1717+ id: "navbar",
1818+ Link {
1919+ to: Route::Home {},
2020+ "Home"
2121+ }
2222+ Link {
2323+ to: Route::Blog { id: 1 },
2424+ "Blog"
2525+ }
2626+ }
2727+2828+ // The `Outlet` component is used to render the next component inside the layout. In this case, it will render either
2929+ // the [`Home`] or [`Blog`] component depending on the current route.
3030+ Outlet::<Route> {}
3131+ }
3232+}