commit 8afc336aa88c8c910f1a2477abd45bb4e854dcc3
parent fa673deaa990eb1626502f8af28d584f71404aa8
Author: triesap <tyson@radroots.org>
Date: Fri, 20 Mar 2026 16:27:14 +0000
build: bootstrap wasm target
Diffstat:
13 files changed, 228 insertions(+), 19 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,4 +1,7 @@
/target/
+/.trunk/
+/crates/app-web/.trunk/
+/crates/app-web/dist/
# Local environment files
.env
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
@@ -12,6 +12,13 @@ Install the Rust toolchain used by this repository:
```bash
rustup toolchain install 1.92.0
+rustup target add wasm32-unknown-unknown
+```
+
+Install Trunk for the wasm target:
+
+```bash
+cargo install trunk
```
Confirm your environment:
@@ -19,6 +26,7 @@ Confirm your environment:
```bash
cargo --version
rustc --version
+trunk --version
```
## Getting Started
@@ -64,6 +72,26 @@ Run the native application:
cargo run -p radroots-app
```
+Check the wasm application:
+
+```bash
+env -u NO_COLOR cargo check -p radroots-app-web --target wasm32-unknown-unknown
+```
+
+Build the wasm application:
+
+```bash
+cd crates/app-web
+env -u NO_COLOR trunk build
+```
+
+Run the wasm application:
+
+```bash
+cd crates/app-web
+env -u NO_COLOR trunk serve --open
+```
+
## Contribution Guidelines
- Keep changes scoped to a single coherent change.
diff --git a/Cargo.lock b/Cargo.lock
@@ -322,6 +322,8 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [
+ "serde",
+ "termcolor",
"unicode-width",
]
@@ -1992,6 +1994,18 @@ dependencies = [
]
[[package]]
+name = "radroots-app-web"
+version = "0.1.0"
+dependencies = [
+ "eframe",
+ "log",
+ "radroots-app",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu",
+]
+
+[[package]]
name = "range-alloc"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2311,6 +2325,15 @@ dependencies = [
]
[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2745,12 +2768,17 @@ dependencies = [
"cfg_aliases",
"document-features",
"hashbrown 0.16.1",
+ "js-sys",
"log",
+ "naga",
"portable-atomic",
"profiling",
"raw-window-handle",
"smallvec",
"static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
"wgpu-core",
"wgpu-hal",
"wgpu-types",
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
members = [
- "crates/app"
+ "crates/app",
+ "crates/app-web",
]
resolver = "2"
@@ -17,7 +18,10 @@ readme = "README.md"
[workspace.dependencies]
eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "wgpu", "wayland", "x11"] }
egui = { version = "0.33.3", features = ["serde"] }
+log = "0.4.28"
objc2-foundation = { version = "0.3.2", default-features = false, features = ["std"] }
+wasm-bindgen-futures = "0.4.50"
+web-sys = { version = "0.3.91", features = ["Document", "HtmlCanvasElement", "Window"] }
wgpu = { version = "27.0.1", default-features = false }
[workspace.lints.rust]
diff --git a/crates/app-web/Cargo.toml b/crates/app-web/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "radroots-app-web"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+rust-version.workspace = true
+authors.workspace = true
+repository.workspace = true
+homepage.workspace = true
+description = "Rad Roots app wasm launcher"
+publish = false
+
+[lib]
+path = "src/lib.rs"
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+eframe.workspace = true
+log.workspace = true
+radroots-app = { path = "../app" }
+wasm-bindgen-futures.workspace = true
+web-sys.workspace = true
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+wgpu = { workspace = true, features = ["std", "wgsl", "webgpu", "fragile-send-sync-non-atomic-wasm"] }
+
+[lints]
+workspace = true
diff --git a/crates/app-web/Trunk.toml b/crates/app-web/Trunk.toml
@@ -0,0 +1,3 @@
+[build]
+target = "index.html"
+dist = "dist"
diff --git a/crates/app-web/index.html b/crates/app-web/index.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta
+ name="viewport"
+ content="width=device-width, initial-scale=1.0, viewport-fit=cover"
+ />
+ <title>Rad Roots</title>
+ <base data-trunk-public-url />
+ <style>
+ html,
+ body {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ background: #0e1116;
+ color: #f4efe1;
+ font-family: ui-sans-serif, system-ui, sans-serif;
+ }
+
+ body {
+ display: grid;
+ place-items: center;
+ }
+
+ #app-shell {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ }
+
+ #radroots_app_canvas {
+ width: 100%;
+ height: 100%;
+ display: block;
+ }
+
+ #loading_text {
+ position: absolute;
+ inset: 0;
+ display: grid;
+ place-items: center;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ font-size: 0.75rem;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="app-shell">
+ <canvas id="radroots_app_canvas"></canvas>
+ <div id="loading_text">loading rad roots…</div>
+ </div>
+ <link data-trunk rel="rust" href="Cargo.toml" data-bin="radroots-app-web" />
+ </body>
+</html>
diff --git a/crates/app-web/src/lib.rs b/crates/app-web/src/lib.rs
@@ -0,0 +1,44 @@
+#![forbid(unsafe_code)]
+
+#[cfg(target_arch = "wasm32")]
+use eframe::wasm_bindgen::JsCast as _;
+
+#[cfg(target_arch = "wasm32")]
+pub fn launch() {
+ let log_level = if cfg!(debug_assertions) {
+ log::LevelFilter::Info
+ } else {
+ log::LevelFilter::Warn
+ };
+ let _ = eframe::WebLogger::init(log_level);
+
+ wasm_bindgen_futures::spawn_local(async {
+ let web_options = eframe::WebOptions::default();
+ let window = web_sys::window().expect("window");
+ let document = window.document().expect("document");
+ let canvas = document
+ .get_element_by_id("radroots_app_canvas")
+ .expect("radroots_app_canvas")
+ .dyn_into::<web_sys::HtmlCanvasElement>()
+ .expect("canvas");
+
+ let result = eframe::WebRunner::new()
+ .start(
+ canvas,
+ web_options,
+ Box::new(|_cc| Ok(Box::new(radroots_app::RadrootsApp))),
+ )
+ .await;
+
+ if let Some(loading_text) = document.get_element_by_id("loading_text") {
+ if result.is_ok() {
+ loading_text.remove();
+ } else {
+ loading_text.set_inner_html("<p>failed to start radroots app</p>");
+ }
+ }
+ });
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+pub fn launch() {}
diff --git a/crates/app-web/src/main.rs b/crates/app-web/src/main.rs
@@ -0,0 +1,5 @@
+#![forbid(unsafe_code)]
+
+fn main() {
+ radroots_app_web::launch();
+}
diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml
@@ -10,6 +10,9 @@ homepage.workspace = true
description = "Rad Roots app"
publish = false
+[lib]
+path = "src/lib.rs"
+
[lints]
workspace = true
diff --git a/crates/app/src/lib.rs b/crates/app/src/lib.rs
@@ -0,0 +1,21 @@
+#![forbid(unsafe_code)]
+
+use eframe::egui;
+
+pub const APP_NAME: &str = "Rad Roots";
+
+#[derive(Default)]
+pub struct RadrootsApp;
+
+impl eframe::App for RadrootsApp {
+ fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
+ egui::CentralPanel::default().show(ctx, |ui| {
+ ui.vertical_centered(|ui| {
+ ui.add_space(48.0);
+ ui.heading(APP_NAME);
+ ui.add_space(12.0);
+ ui.label("radroots app");
+ });
+ });
+ }
+}
diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs
@@ -2,8 +2,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use eframe::egui;
-
-const APP_NAME: &str = "Rad Roots";
+use radroots_app::{APP_NAME, RadrootsApp};
#[cfg(target_os = "macos")]
fn set_macos_app_name() {
@@ -17,22 +16,6 @@ fn set_macos_app_name() {
#[cfg(not(target_os = "macos"))]
fn set_macos_app_name() {}
-#[derive(Default)]
-struct RadrootsApp;
-
-impl eframe::App for RadrootsApp {
- fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
- egui::CentralPanel::default().show(ctx, |ui| {
- ui.vertical_centered(|ui| {
- ui.add_space(48.0);
- ui.heading(APP_NAME);
- ui.add_space(12.0);
- ui.label("radroots app");
- });
- });
- }
-}
-
fn main() -> eframe::Result<()> {
set_macos_app_name();
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
@@ -1,2 +1,3 @@
[toolchain]
channel = "1.92.0"
+targets = ["wasm32-unknown-unknown"]