diff --git a/Cargo.lock b/Cargo.lock
index 3c34e0f6..efa9b978 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,6 +18,7 @@ version = "0.5.7"
dependencies = [
"acadrust",
"bytemuck",
+ "console_error_panic_hook",
"cosmic-text",
"flate2",
"fontdb",
@@ -977,6 +978,16 @@ dependencies = [
"crossbeam-utils",
]
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
[[package]]
name = "convert_case"
version = "0.4.0"
diff --git a/Cargo.toml b/Cargo.toml
index fae4e8e2..15a5c814 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -79,3 +79,6 @@ acadrust = { git = "https://github.com/HakanSeven12/acadrust", branch = "main" }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
open = "5"
ureq = { version = "3", default-features = false, features = ["rustls"] }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+console_error_panic_hook = "0.1"
diff --git a/Trunk.toml b/Trunk.toml
new file mode 100644
index 00000000..e58869cd
--- /dev/null
+++ b/Trunk.toml
@@ -0,0 +1,16 @@
+# Trunk config for the web (wasm) build of Open CAD Studio (issue #45).
+#
+# Build: trunk build --release
+# Dev: trunk serve
+#
+# Requires: rustup target add wasm32-unknown-unknown
+# cargo install trunk wasm-bindgen-cli
+
+[build]
+target = "index.html"
+
+[serve]
+# Cross-origin isolation headers enable SharedArrayBuffer, which wasm threads
+# (rayon) need. GitHub Pages cannot set these; a self-hosted server (e.g. the
+# Open-AEC host) can. Without them the app still runs single-threaded.
+headers = { "Cross-Origin-Opener-Policy" = "same-origin", "Cross-Origin-Embedder-Policy" = "require-corp" }
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..a58c5347
--- /dev/null
+++ b/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Open CAD Studio
+
+
+
+
+
+
diff --git a/src/app/mod.rs b/src/app/mod.rs
index ae19fe7e..67b2e295 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -1670,10 +1670,21 @@ impl OpenCADStudio {
Task::batch([open_main, check_update, focus_cmd, cli_open, assoc_prompt]),
)
}
+
+ /// Single-window boot for the web build: no OS-window creation (the browser
+ /// canvas is the only window), no file-association registration or CLI file
+ /// open. Secondary manager windows are unavailable on the web for now.
+ #[cfg(target_arch = "wasm32")]
+ fn boot_web() -> (Self, Task) {
+ let mut s = Self::new();
+ let focus = s.focus_cmd_input();
+ (s, focus)
+ }
}
use std::path::PathBuf;
+#[cfg(not(target_arch = "wasm32"))]
pub fn run() -> iced::Result {
iced::daemon(
OpenCADStudio::boot,
@@ -1744,3 +1755,20 @@ pub fn run() -> iced::Result {
.theme(|state: &OpenCADStudio, _| state.active_theme.clone())
.run()
}
+
+/// Single-window entry for the web (wasm) build. Uses `iced::application`
+/// instead of `iced::daemon`: the browser canvas is the only window, so the
+/// main-window view is rendered directly and the manager/dialog windows are
+/// unavailable for now (see issue #45). Native keeps the multi-window `run`.
+#[cfg(target_arch = "wasm32")]
+pub fn run_web() -> iced::Result {
+ iced::application(
+ OpenCADStudio::boot_web,
+ OpenCADStudio::update,
+ OpenCADStudio::view_main,
+ )
+ .subscription(OpenCADStudio::subscription)
+ .title(|_state: &OpenCADStudio| "Open CAD Studio".to_string())
+ .theme(|state: &OpenCADStudio| state.active_theme.clone())
+ .run()
+}
diff --git a/src/app/view.rs b/src/app/view.rs
index 75339340..44703435 100644
--- a/src/app/view.rs
+++ b/src/app/view.rs
@@ -511,6 +511,13 @@ impl OpenCADStudio {
);
}
+ self.view_main()
+ }
+
+ /// The primary window: viewport, ribbon, tab bar, status bar. Split out of
+ /// `view` so the single-window web build can render it directly, bypassing
+ /// the multi-window id dispatch above (the web build has no extra windows).
+ pub fn view_main(&self) -> Element<'_, Message> {
let i = self.active_tab;
let tab = &self.tabs[i];
let is_paper = tab.scene.current_layout != "Model";
diff --git a/src/main.rs b/src/main.rs
index 0a3427e9..c7cff8cf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -28,5 +28,16 @@ fn main() -> iced::Result {
if std::env::var_os("WGPU_BACKEND").is_none() {
std::env::set_var("WGPU_BACKEND", "dx12,vulkan");
}
- app::run()
+
+ // Web (wasm) uses the single-window entry; native uses the multi-window
+ // daemon. Trunk calls `main` from its generated JS bootstrap.
+ #[cfg(target_arch = "wasm32")]
+ {
+ console_error_panic_hook::set_once();
+ app::run_web()
+ }
+ #[cfg(not(target_arch = "wasm32"))]
+ {
+ app::run()
+ }
}