diff --git a/crates/svg/src/import/depth.rs b/crates/svg/src/import/depth.rs index 3febdd96..a172affe 100644 --- a/crates/svg/src/import/depth.rs +++ b/crates/svg/src/import/depth.rs @@ -20,8 +20,22 @@ use super::css::{Styles, XNode, parse_decls}; /// that hold one object aren't kept, and what is still deeper is merged into the group around /// it). usvg uses about 15 kB of stack for each level: the importer runs on a thread with 16 MB /// ([`vectorcraft_doc::depth::with_deep_stack`]), except on the web, where there is the one -/// 1 MB stack. -pub const MAX_ELEMENT_DEPTH: usize = if cfg!(target_arch = "wasm32") { 48 } else { 256 }; +/// stack: [`WEB_DEPTH`] there. +pub const MAX_ELEMENT_DEPTH: usize = if cfg!(target_arch = "wasm32") { WEB_DEPTH } else { 256 }; + +/// The stack of the web build: rustc links `wasm32-unknown-unknown` with +/// `-z stack-size=1048576` (`rustc --print link-args` shows it), and the web app gives the linker +/// no arguments of its own (`apps/vectorcraft-web`: `Trunk.toml`, `index.html`, no rustflags). +const WEB_STACK: usize = 1 << 20; + +/// The stack usvg uses for each level of elements: about 70 levels overflowed the 1 MB main +/// thread of Windows. Measured natively, never in a browser. +const LEVEL_STACK: usize = 15 << 10; + +/// The deepest elements may nest on the web: half its stack at usvg's cost a level, the other +/// half left for what is on the stack when a file is opened and for the cost being a native +/// measure. 1,048,576 / (2 × 15,360) = 34. +const WEB_DEPTH: usize = WEB_STACK / (2 * LEVEL_STACK); /// Properties whose value an element's descendants use too: the paint server they name is /// converted at each descendant, under everything between. diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 4030165e..abcd42af 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -25,7 +25,7 @@ commands: assets check that every icon/image/font/asset is attributed in ASSETS.md brands check that user-visible text (labels, params docs, MCP, docs, packaging) names no other vendor's products layers enforce the crate dependency layering (plan/architecture.md §3) - wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates + wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates and the web app ci fmt --check, clippy -D warnings, test, assets, brands, layers, wasm (stops at first failure) corpus [--download] [--affinity] show where test corpora live; --download fetches PngSuite into corpus/pngsuite, @@ -141,8 +141,8 @@ fn cmd_layers() -> Result<(), String> { } } -/// Workspace packages that must build for wasm32: all L0–L5 crates plus -/// the egui shell. +/// Workspace packages that must build for wasm32: all L0–L5 crates, the egui +/// shell, and the web app (its wasm-only code is compiled by nothing else). fn wasm_set() -> Result, String> { let crates = layers::from_metadata(&metadata()?)?; Ok(crates @@ -150,7 +150,7 @@ fn wasm_set() -> Result, String> { .filter(|c| match layers::classify(&c.name) { Some(layers::Class::Layer(l)) => l <= 5 || layers::short_name(&c.name) == "ui-egui", Some(layers::Class::Standalone) => true, - _ => false, + _ => layers::short_name(&c.name) == "web", }) .map(|c| c.name) .collect())