BREP_app 0.3.0

The BREP CAD application: an eframe (egui + wgpu) host that draws the brep-render 3D engine into an egui frame — native + wasm from one codebase.
Documentation
//! brep-app — engine-native UI spike (an eframe host for the brep-render engine).
//!
//! The same [`app::BrepApp`] runs on native (`main.rs` → `run_native`) and on
//! the web (`start` below → `eframe::WebRunner`), sharing one wgpu frame with
//! egui. Additive spike: it does NOT touch the previous app or the render wasm build.

pub mod app;
pub mod column_tree;
pub mod document;
pub mod fonts;
pub mod form;
pub mod form_view;
pub mod icon_text;
pub mod icons;
pub mod palette;
pub mod panels;
pub mod recovery;
pub mod store;
pub mod viewport;
pub mod workbench;

// The wasm history runner: a dedicated web worker so a history run stays OFF the
// browser main thread (single-threaded wasm) and the UI never freezes during a run.
// Its `worker_entry` is the worker-side onmessage loop. wasm only.
#[cfg(target_arch = "wasm32")]
pub mod worker;

// --- wasm entry ----------------------------------------------------------------
#[cfg(target_arch = "wasm32")]
mod web {
    use wasm_bindgen::prelude::*;
    use wasm_bindgen::JsCast;

    /// Start the eframe app on the given `<canvas>` element id. Called from JS.
    #[wasm_bindgen]
    pub async fn start(canvas_id: String) -> Result<(), JsValue> {
        console_error_panic_hook::set_once();

        // Bring up browser persistence FIRST. `ModelStore` is synchronous but every
        // browser store large enough for a native BREP payload is async, so the
        // whole key space is pulled into an in-memory mirror here — inside the one
        // async seam the app has — BEFORE `BrepApp::new` performs its first read.
        // See store.rs `mirror_store`.
        crate::store::hydrate_web_store().await;

        let document = web_sys::window()
            .ok_or_else(|| JsValue::from_str("no window"))?
            .document()
            .ok_or_else(|| JsValue::from_str("no document"))?;
        let canvas = document
            .get_element_by_id(&canvas_id)
            .ok_or_else(|| JsValue::from_str("canvas element not found"))?
            .dyn_into::<web_sys::HtmlCanvasElement>()
            .map_err(|_| JsValue::from_str("element is not a <canvas>"))?;

        eframe::WebRunner::new()
            .start(
                canvas,
                eframe::WebOptions::default(),
                Box::new(|cc| {
                    crate::fonts::install(&cc.egui_ctx);
                    crate::app::BrepApp::new(cc)
                        .map(|app| Box::new(app) as Box<dyn eframe::App>)
                        .map_err(|e| e.into())
                }),
            )
            .await
    }
}