commit 485b9c675863af63036cc13b5dc32957a5412073
parent 8afc336aa88c8c910f1a2477abd45bb4e854dcc3
Author: triesap <tyson@radroots.org>
Date: Fri, 20 Mar 2026 17:34:35 +0000
build: split bootstrap workspace into core desktop and web
Diffstat:
18 files changed, 173 insertions(+), 150 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,7 +1,7 @@
/target/
/.trunk/
-/crates/app-web/.trunk/
-/crates/app-web/dist/
+/crates/web/.trunk/
+/crates/web/dist/
# Local environment files
.env
diff --git a/AGENTS.md b/AGENTS.md
@@ -41,13 +41,14 @@ Before making substantial changes:
- `cargo metadata --format-version 1 --no-deps`
- `cargo check`
- targeted `cargo test`
- - targeted `cargo run -p radroots-app`
+ - targeted `cargo run -p radroots-app-desktop`
- If validation cannot be run, report the blocker clearly.
## 6. Workspace structure
- Keep the repository root as the workspace root.
-- Keep the main application crate under `crates/app`.
+- Keep shared application code under `crates/core`.
+- Keep target launchers and bridge crates under `crates/`.
- Add new crates only when they represent a durable architectural boundary.
- Keep manifests, paths, and crate boundaries simple and intentional.
- Do not reintroduce obsolete framework scaffolding unless the requested change explicitly requires it.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
@@ -69,7 +69,7 @@ cargo test
Run the native application:
```bash
-cargo run -p radroots-app
+cargo run -p radroots-app-desktop
```
Check the wasm application:
@@ -81,14 +81,14 @@ env -u NO_COLOR cargo check -p radroots-app-web --target wasm32-unknown-unknown
Build the wasm application:
```bash
-cd crates/app-web
+cd crates/web
env -u NO_COLOR trunk build
```
Run the wasm application:
```bash
-cd crates/app-web
+cd crates/web
env -u NO_COLOR trunk serve --open
```
diff --git a/Cargo.lock b/Cargo.lock
@@ -1984,12 +1984,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
-name = "radroots-app"
+name = "radroots-app-core"
+version = "0.1.0"
+dependencies = [
+ "eframe",
+ "egui",
+]
+
+[[package]]
+name = "radroots-app-desktop"
version = "0.1.0"
dependencies = [
"eframe",
"egui",
"objc2-foundation 0.3.2",
+ "radroots-app-core",
"wgpu",
]
@@ -1999,7 +2008,7 @@ version = "0.1.0"
dependencies = [
"eframe",
"log",
- "radroots-app",
+ "radroots-app-core",
"wasm-bindgen-futures",
"web-sys",
"wgpu",
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,7 +1,8 @@
[workspace]
members = [
- "crates/app",
- "crates/app-web",
+ "crates/core",
+ "crates/desktop",
+ "crates/web",
]
resolver = "2"
diff --git a/crates/app-web/Cargo.toml b/crates/app-web/Cargo.toml
@@ -1,28 +0,0 @@
-[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/src/lib.rs b/crates/app-web/src/lib.rs
@@ -1,44 +0,0 @@
-#![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/Cargo.toml b/crates/app/Cargo.toml
@@ -1,33 +0,0 @@
-[package]
-name = "radroots-app"
-authors.workspace = true
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-rust-version.workspace = true
-repository.workspace = true
-homepage.workspace = true
-description = "Rad Roots app"
-publish = false
-
-[lib]
-path = "src/lib.rs"
-
-[lints]
-workspace = true
-
-[dependencies]
-eframe.workspace = true
-egui.workspace = true
-
-[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
-wgpu = { workspace = true, features = ["metal", "wgsl"] }
-
-[target.'cfg(target_os = "macos")'.dependencies]
-objc2-foundation = { workspace = true, features = ["NSProcessInfo", "NSString"] }
-
-[target.'cfg(target_os = "windows")'.dependencies]
-wgpu = { workspace = true, features = ["dx12", "wgsl"] }
-
-[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
-wgpu = { workspace = true, features = ["vulkan", "gles", "wgsl"] }
diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs
@@ -1,34 +0,0 @@
-#![forbid(unsafe_code)]
-#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-
-use eframe::egui;
-use radroots_app::{APP_NAME, RadrootsApp};
-
-#[cfg(target_os = "macos")]
-fn set_macos_app_name() {
- use objc2_foundation::{NSProcessInfo, NSString};
-
- let process_info = NSProcessInfo::processInfo();
- let process_name = NSString::from_str(APP_NAME);
- process_info.setProcessName(&process_name);
-}
-
-#[cfg(not(target_os = "macos"))]
-fn set_macos_app_name() {}
-
-fn main() -> eframe::Result<()> {
- set_macos_app_name();
-
- let options = eframe::NativeOptions {
- viewport: egui::ViewportBuilder::default()
- .with_inner_size([1280.0, 820.0])
- .with_min_inner_size([480.0, 320.0]),
- ..Default::default()
- };
-
- eframe::run_native(
- APP_NAME,
- options,
- Box::new(|_cc| Ok(Box::new(RadrootsApp))),
- )
-}
diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "radroots-app-core"
+authors.workspace = true
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+rust-version.workspace = true
+repository.workspace = true
+homepage.workspace = true
+description = "Rad Roots application core"
+publish = false
+
+[lints]
+workspace = true
+
+[dependencies]
+eframe.workspace = true
+egui.workspace = true
diff --git a/crates/app/src/lib.rs b/crates/core/src/lib.rs
diff --git a/crates/desktop/Cargo.toml b/crates/desktop/Cargo.toml
@@ -0,0 +1,31 @@
+[package]
+name = "radroots-app-desktop"
+authors.workspace = true
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+rust-version.workspace = true
+repository.workspace = true
+homepage.workspace = true
+description = "Rad Roots desktop launcher"
+publish = false
+
+[lints]
+workspace = true
+
+[dependencies]
+eframe.workspace = true
+egui.workspace = true
+radroots-app-core = { path = "../core" }
+
+[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
+wgpu = { workspace = true, features = ["metal", "wgsl"] }
+
+[target.'cfg(target_os = "macos")'.dependencies]
+objc2-foundation = { workspace = true, features = ["NSProcessInfo", "NSString"] }
+
+[target.'cfg(target_os = "windows")'.dependencies]
+wgpu = { workspace = true, features = ["dx12", "wgsl"] }
+
+[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
+wgpu = { workspace = true, features = ["vulkan", "gles", "wgsl"] }
diff --git a/crates/desktop/src/main.rs b/crates/desktop/src/main.rs
@@ -0,0 +1,30 @@
+#![forbid(unsafe_code)]
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+use eframe::egui;
+use radroots_app_core::{APP_NAME, RadrootsApp};
+
+#[cfg(target_os = "macos")]
+fn set_macos_app_name() {
+ use objc2_foundation::{NSProcessInfo, NSString};
+
+ let process_info = NSProcessInfo::processInfo();
+ let process_name = NSString::from_str(APP_NAME);
+ process_info.setProcessName(&process_name);
+}
+
+#[cfg(not(target_os = "macos"))]
+fn set_macos_app_name() {}
+
+fn main() -> eframe::Result<()> {
+ set_macos_app_name();
+
+ let options = eframe::NativeOptions {
+ viewport: egui::ViewportBuilder::default()
+ .with_inner_size([1280.0, 820.0])
+ .with_min_inner_size([480.0, 320.0]),
+ ..Default::default()
+ };
+
+ eframe::run_native(APP_NAME, options, Box::new(|_cc| Ok(Box::new(RadrootsApp))))
+}
diff --git a/crates/web/Cargo.toml b/crates/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 web launcher"
+publish = false
+
+[lib]
+path = "src/lib.rs"
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+eframe.workspace = true
+log.workspace = true
+radroots-app-core = { path = "../core" }
+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/web/Trunk.toml
diff --git a/crates/app-web/index.html b/crates/web/index.html
diff --git a/crates/web/src/lib.rs b/crates/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_core::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/web/src/main.rs