build(web): restore the wasm build — gate native-only deps off wasm
The web build had bit-rotted: native-only features added since it last compiled pulled crates that don't build for wasm32-unknown-unknown. - printpdf (PDF export / printing) → native-only target; the web build gets stub export_pdf / print_wires. It pulls a wasm-incompatible memchr 1.0.2 via lopdf → nom_locate, and the web has no filesystem. - getrandom 0.3 (via ahash via acadrust) → enable its wasm_js feature and add the matching --cfg getrandom_backend rustflag in .cargo/config.toml. - ocs_plugin_api "host" feature (out-of-process plugin runtime: interprocess, libloading, memmap2, rkyv) → native-only target; the web gets the dependency-free manifest/ribbon contract. The host code was already wasm-gated; only the Cargo wiring pulled it in unconditionally. `trunk build --release` succeeds again. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8b1e37e0da
commit
44fa9df278
5 changed files with 75 additions and 3 deletions
6
.cargo/config.toml
Normal file
6
.cargo/config.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# getrandom 0.3 (pulled by ahash via acadrust) needs this cfg to select its
|
||||||
|
# browser backend on wasm32-unknown-unknown, alongside its `wasm_js` feature
|
||||||
|
# (enabled in Cargo.toml for the wasm target). Without it the web build fails
|
||||||
|
# with "wasm32-unknown-unknown targets are not supported by default".
|
||||||
|
[target.wasm32-unknown-unknown]
|
||||||
|
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']
|
||||||
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -24,6 +24,7 @@ dependencies = [
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"flate2",
|
"flate2",
|
||||||
"fontdb",
|
"fontdb",
|
||||||
|
"getrandom 0.3.4",
|
||||||
"glam 0.33.0",
|
"glam 0.33.0",
|
||||||
"iced",
|
"iced",
|
||||||
"image",
|
"image",
|
||||||
|
|
|
||||||
16
Cargo.toml
16
Cargo.toml
|
|
@ -24,7 +24,9 @@ solid3d = ["dep:truck-meshalgo", "dep:truck-shapeops", "dep:lzma-sys"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Stable, dependency-free add-on contract (manifest + ribbon/CadModule types).
|
# Stable, dependency-free add-on contract (manifest + ribbon/CadModule types).
|
||||||
# Plugin authors target this crate's semver, not OpenCADStudio internals.
|
# Plugin authors target this crate's semver, not OpenCADStudio internals.
|
||||||
ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] }
|
# The `host` feature (out-of-process plugin runtime: interprocess, libloading,
|
||||||
|
# memmap2, rkyv) is native-only — see the per-target sections below; the web
|
||||||
|
# build gets the dependency-free manifest/ribbon contract.
|
||||||
# vtkio (pulled in transitively via truck-meshalgo) depends on xz2 → lzma-sys,
|
# vtkio (pulled in transitively via truck-meshalgo) depends on xz2 → lzma-sys,
|
||||||
# which by default links the system liblzma dynamically. On macOS that bakes a
|
# which by default links the system liblzma dynamically. On macOS that bakes a
|
||||||
# Homebrew dylib path (/opt/homebrew/.../liblzma.5.dylib) into the binary, so
|
# Homebrew dylib path (/opt/homebrew/.../liblzma.5.dylib) into the binary, so
|
||||||
|
|
@ -44,7 +46,6 @@ clap = { version = "4", features = ["derive"] }
|
||||||
# Opt-in logging via --log / RUST_LOG (surfaces wgpu / iced / winit diagnostics).
|
# Opt-in logging via --log / RUST_LOG (surfaces wgpu / iced / winit diagnostics).
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
acadrust = "0.3.4"
|
acadrust = "0.3.4"
|
||||||
printpdf = "0.9.1"
|
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||||
inventory = "0.3"
|
inventory = "0.3"
|
||||||
|
|
@ -80,14 +81,25 @@ windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_Window
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/acadrust", branch = "main" }
|
acadrust = { git = "https://github.com/HakanSeven12/acadrust", branch = "main" }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
# Native enables the plugin host runtime (out-of-process plugins).
|
||||||
|
ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] }
|
||||||
rayon = "1"
|
rayon = "1"
|
||||||
open = "5"
|
open = "5"
|
||||||
|
# PDF export / printing. Native-only: printpdf pulls a wasm-incompatible
|
||||||
|
# `memchr` (1.0.2) via lopdf → nom_locate, and the web build has no filesystem.
|
||||||
|
printpdf = "0.9.1"
|
||||||
ureq = { version = "3", default-features = false, features = ["rustls"] }
|
ureq = { version = "3", default-features = false, features = ["rustls"] }
|
||||||
# Parse the GitHub Releases API response for the plugin marketplace.
|
# Parse the GitHub Releases API response for the plugin marketplace.
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
# Web gets the dependency-free manifest/ribbon contract only (no plugin host).
|
||||||
|
ocs_plugin_api = { path = "crates/ocs_plugin_api" }
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
|
# ahash (via acadrust) pulls getrandom 0.3, which needs the `wasm_js` backend
|
||||||
|
# enabled to support wasm32-unknown-unknown (plus the matching rustflag in
|
||||||
|
# .cargo/config.toml).
|
||||||
|
getrandom = { version = "0.3", features = ["wasm_js"] }
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
# fetch() returns a JS Promise; bridge it to a Rust future for the per-script
|
# fetch() returns a JS Promise; bridge it to a Rust future for the per-script
|
||||||
# web font loader (#141).
|
# web font loader (#141).
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,43 @@
|
||||||
// drawing origin at the paper origin.
|
// drawing origin at the paper origin.
|
||||||
|
|
||||||
use crate::io::plot_style::PlotStyleTable;
|
use crate::io::plot_style::PlotStyleTable;
|
||||||
use crate::scene::model::hatch_model::{HatchModel, HatchPattern};
|
use crate::scene::model::hatch_model::HatchModel;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use crate::scene::model::hatch_model::HatchPattern;
|
||||||
use crate::scene::WireModel;
|
use crate::scene::WireModel;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use printpdf::{
|
use printpdf::{
|
||||||
Color, Line, LineCapStyle, LineJoinStyle, LinePoint, Mm, Op, PaintMode, PdfDocument, PdfPage,
|
Color, Line, LineCapStyle, LineJoinStyle, LinePoint, Mm, Op, PaintMode, PdfDocument, PdfPage,
|
||||||
PdfSaveOptions, Point, Polygon, PolygonRing, Pt, Rgb, WindingOrder,
|
PdfSaveOptions, Point, Polygon, PolygonRing, Pt, Rgb, WindingOrder,
|
||||||
};
|
};
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
// The web build has no `printpdf` (it pulls a wasm-incompatible `memchr` via
|
||||||
|
// lopdf → nom_locate) and no filesystem, so PDF export is native-only; the web
|
||||||
|
// build gets these stubs so the call sites still compile.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub fn export_pdf(
|
||||||
|
_wires: &[WireModel],
|
||||||
|
_hatches: &[HatchModel],
|
||||||
|
_wipeouts: &[HatchModel],
|
||||||
|
_paper_w: f64,
|
||||||
|
_paper_h: f64,
|
||||||
|
_offset_x: f32,
|
||||||
|
_offset_y: f32,
|
||||||
|
_rotation_deg: i32,
|
||||||
|
_path: &Path,
|
||||||
|
_plot_style: Option<&PlotStyleTable>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("PDF export is not available in the web version.".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub async fn pick_pdf_path_owned(_stem: String) -> Option<std::path::PathBuf> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
// ── Public entry point ────────────────────────────────────────────────────
|
// ── Public entry point ────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Export `wires` to a PDF file.
|
/// Export `wires` to a PDF file.
|
||||||
|
|
@ -26,6 +54,7 @@ use std::path::Path;
|
||||||
/// - `offset_x` / `offset_y`: added to every wire coordinate so the drawing
|
/// - `offset_x` / `offset_y`: added to every wire coordinate so the drawing
|
||||||
/// origin maps to the bottom-left corner of the page.
|
/// origin maps to the bottom-left corner of the page.
|
||||||
/// - `rotation_deg`: 0 | 90 | 180 | 270 — rotates the entire drawing on the page.
|
/// - `rotation_deg`: 0 | 90 | 180 | 270 — rotates the entire drawing on the page.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub fn export_pdf(
|
pub fn export_pdf(
|
||||||
wires: &[WireModel],
|
wires: &[WireModel],
|
||||||
hatches: &[HatchModel],
|
hatches: &[HatchModel],
|
||||||
|
|
@ -54,6 +83,7 @@ pub fn export_pdf(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show a PDF save-file dialog and return the chosen path (or None if cancelled).
|
/// Show a PDF save-file dialog and return the chosen path (or None if cancelled).
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub async fn pick_pdf_path_owned(stem: String) -> Option<std::path::PathBuf> {
|
pub async fn pick_pdf_path_owned(stem: String) -> Option<std::path::PathBuf> {
|
||||||
rfd::AsyncFileDialog::new()
|
rfd::AsyncFileDialog::new()
|
||||||
.set_title("Export as PDF")
|
.set_title("Export as PDF")
|
||||||
|
|
@ -67,6 +97,7 @@ pub async fn pick_pdf_path_owned(stem: String) -> Option<std::path::PathBuf> {
|
||||||
|
|
||||||
// ── PDF builder ───────────────────────────────────────────────────────────
|
// ── PDF builder ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn build_pdf(
|
fn build_pdf(
|
||||||
wires: &[WireModel],
|
wires: &[WireModel],
|
||||||
hatches: &[HatchModel],
|
hatches: &[HatchModel],
|
||||||
|
|
@ -235,6 +266,7 @@ fn build_pdf(
|
||||||
doc.save(&PdfSaveOptions::default(), &mut warnings)
|
doc.save(&PdfSaveOptions::default(), &mut warnings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn flush_line(ops: &mut Vec<Op>, pts: &[LinePoint]) {
|
fn flush_line(ops: &mut Vec<Op>, pts: &[LinePoint]) {
|
||||||
if pts.len() < 2 {
|
if pts.len() < 2 {
|
||||||
return;
|
return;
|
||||||
|
|
@ -252,6 +284,7 @@ fn flush_line(ops: &mut Vec<Op>, pts: &[LinePoint]) {
|
||||||
/// rings so islands and holes render correctly under the even-odd rule.
|
/// rings so islands and holes render correctly under the even-odd rule.
|
||||||
/// Mirrors `scene::paper_canvas::draw_hatch`: solid → fill, pattern → outline,
|
/// Mirrors `scene::paper_canvas::draw_hatch`: solid → fill, pattern → outline,
|
||||||
/// gradient → solid fill of the averaged colour.
|
/// gradient → solid fill of the averaged colour.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn emit_hatch(ops: &mut Vec<Op>, hatch: &HatchModel, ox: f32, oy: f32) {
|
fn emit_hatch(ops: &mut Vec<Op>, hatch: &HatchModel, ox: f32, oy: f32) {
|
||||||
if hatch.boundary.is_empty() {
|
if hatch.boundary.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,34 @@
|
||||||
//
|
//
|
||||||
// The function is async so the UI remains responsive while the job is queued.
|
// The function is async so the UI remains responsive while the job is queued.
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use crate::io::pdf_export;
|
use crate::io::pdf_export;
|
||||||
use crate::io::plot_style::PlotStyleTable;
|
use crate::io::plot_style::PlotStyleTable;
|
||||||
use crate::scene::model::hatch_model::HatchModel;
|
use crate::scene::model::hatch_model::HatchModel;
|
||||||
use crate::scene::WireModel;
|
use crate::scene::WireModel;
|
||||||
|
|
||||||
|
// Printing routes through the native PDF pipeline + the OS print command, so it
|
||||||
|
// is native-only; the web build gets a stub so the call site still compiles.
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub async fn print_wires(
|
||||||
|
_wires: Vec<WireModel>,
|
||||||
|
_hatches: Vec<HatchModel>,
|
||||||
|
_wipeouts: Vec<HatchModel>,
|
||||||
|
_paper_w: f64,
|
||||||
|
_paper_h: f64,
|
||||||
|
_offset_x: f32,
|
||||||
|
_offset_y: f32,
|
||||||
|
_rotation_deg: i32,
|
||||||
|
_plot_style: Option<PlotStyleTable>,
|
||||||
|
) -> Result<String, String> {
|
||||||
|
Err("Printing is not available in the web version.".into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Render `wires` (plus hatch / wipeout fills) to a temp PDF and dispatch it
|
/// Render `wires` (plus hatch / wipeout fills) to a temp PDF and dispatch it
|
||||||
/// to the default system printer.
|
/// to the default system printer.
|
||||||
///
|
///
|
||||||
/// Returns `Ok(printer_name)` on success or `Err(message)` on failure.
|
/// Returns `Ok(printer_name)` on success or `Err(message)` on failure.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub async fn print_wires(
|
pub async fn print_wires(
|
||||||
wires: Vec<WireModel>,
|
wires: Vec<WireModel>,
|
||||||
hatches: Vec<HatchModel>,
|
hatches: Vec<HatchModel>,
|
||||||
|
|
@ -47,6 +66,7 @@ pub async fn print_wires(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Platform-specific dispatch of a PDF path to the system printer.
|
/// Platform-specific dispatch of a PDF path to the system printer.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
fn dispatch_to_printer(path: &std::path::Path) -> Result<String, String> {
|
fn dispatch_to_printer(path: &std::path::Path) -> Result<String, String> {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue