brep_app/lib.rs
1//! Shared eframe application for native windows and WebAssembly canvases.
2//! [`app::BrepApp`] hosts the renderer and egui in one wgpu frame.
3
4pub mod app;
5pub mod automation;
6mod color;
7pub mod column_tree;
8pub mod diagnostics;
9pub mod document;
10/// The three document classes (`.nbrep` / `.fbrep` / `.tbrep`).
11pub mod document_class;
12/// A family seed's table and its Generate (`.fbrep`).
13pub mod family_table;
14/// Register this build as the system's `.nbrep` opener
15/// (`brep-app --install-file-associations`). Native only: the browser has no
16/// file association to claim.
17#[cfg(not(target_arch = "wasm32"))]
18pub mod file_association;
19pub mod handles;
20pub mod fonts;
21pub mod form;
22pub mod form_view;
23pub mod icon_text;
24pub mod icons;
25pub mod instructions;
26mod http;
27/// The native binary's `log` sink (`main.rs`): `RUST_LOG` to stderr. Replaces
28/// `env_logger`; the wasm build logs through the browser console instead.
29#[cfg(not(target_arch = "wasm32"))]
30pub mod logger;
31/// The embedded MCP server (`brep-app --mcp`); native builds with `mcp`.
32#[cfg(all(not(target_arch = "wasm32"), feature = "mcp"))]
33pub mod mcp;
34/// What the Delete key / Delete command removes for the current selection (every target).
35pub mod delete_selection;
36/// The desktop event loop: eframe's winit application plus application-command
37/// dispatch that does not wait for a frame (a hidden Wayland window gets none).
38#[cfg(not(target_arch = "wasm32"))]
39pub mod desktop_loop;
40/// One egui pass drawn into an offscreen texture: the captures of a pass
41/// nothing presents (the desktop loop's, the browser pump's).
42pub mod offscreen;
43/// One egui pass the app runs itself when nothing is drawing it.
44#[cfg(feature = "automation")]
45pub mod on_demand;
46/// The browser's frame-free automation pump: commands answered while the tab
47/// is in the background.
48#[cfg(all(target_arch = "wasm32", feature = "automation"))]
49pub mod web_pump;
50pub mod offsite;
51pub mod palette;
52pub mod panels;
53/// Where a frame's milliseconds go — the per-frame timing record behind the
54/// `__brepPerf` blob and the Info window's Performance rows.
55pub mod perf;
56/// The PLM client: config, transport, sign-in and the PLM store backend. Inert
57/// with no server configured.
58pub mod plm;
59pub mod recovery;
60/// The problem reports this user submitted, and the notice when one is resolved.
61pub mod report_followup;
62/// Scripts in the CAD app: the CAD host for the PLM's interpreter core (S11).
63#[cfg(feature = "scripting")]
64pub mod scripting;
65#[cfg(not(target_arch = "wasm32"))]
66pub mod simulation_export;
67pub mod store;
68/// A template's input marks and its spin-out (`.tbrep`).
69pub mod template;
70/// The desktop app's signed updates from next.brep.io (offered, never silent).
71#[cfg(not(target_arch = "wasm32"))]
72pub mod update;
73pub mod viewport;
74/// The browser's tool surface (WebMCP): the MCP server's tool set, served by
75/// the page as the tools it registers with `document.modelContext`, answered
76/// through the automation queue. The pure half compiles natively for its
77/// tests; the host and the exports `web/webmcp.js` imports exist on wasm only.
78#[cfg(feature = "mcp")]
79pub mod webmcp;
80pub mod workbench;
81pub mod plugins;
82pub mod javascript;
83
84
85// The wasm history runner: a dedicated web worker so a history run stays OFF the
86// browser main thread (single-threaded wasm) and the UI never freezes during a run.
87// Its `worker_entry` is the worker-side onmessage loop. wasm only.
88#[cfg(target_arch = "wasm32")]
89pub mod worker;
90
91// The browser's code worker: one run_script execution on Boa in a Web Worker
92// the page can terminate (`web/code-worker.js` → `code_worker_entry`).
93#[cfg(all(target_arch = "wasm32", feature = "scripting"))]
94pub mod code_worker;
95
96// --- wasm entry ----------------------------------------------------------------
97#[cfg(target_arch = "wasm32")]
98mod web {
99 use wasm_bindgen::prelude::*;
100 use wasm_bindgen::JsCast;
101
102 /// Start the eframe app on the given `<canvas>` element id. Called from JS.
103 #[wasm_bindgen]
104 pub async fn start(canvas_id: String) -> Result<(), JsValue> {
105 brep_render::brep_kernel::panic_hook::set_once();
106
107 // Bring up browser persistence FIRST. `ModelStore` is synchronous but every
108 // browser store large enough for a native BREP payload is async, so the
109 // whole key space is pulled into an in-memory mirror here — inside the one
110 // async seam the app has — BEFORE `BrepApp::new` performs its first read.
111 // See store.rs `mirror_store`.
112 crate::store::hydrate_web_store().await;
113
114 let document = web_sys::window()
115 .ok_or_else(|| JsValue::from_str("no window"))?
116 .document()
117 .ok_or_else(|| JsValue::from_str("no document"))?;
118 let canvas = document
119 .get_element_by_id(&canvas_id)
120 .ok_or_else(|| JsValue::from_str("canvas element not found"))?
121 .dyn_into::<web_sys::HtmlCanvasElement>()
122 .map_err(|_| JsValue::from_str("element is not a <canvas>"))?;
123
124 let runner = eframe::WebRunner::new();
125 // The automation pump borrows the app from this runner between
126 // frames (`web_pump`): commands answer in a background tab too.
127 #[cfg(feature = "automation")]
128 crate::web_pump::set_runner(&runner);
129 runner
130 .start(
131 canvas,
132 eframe::WebOptions::default(),
133 Box::new(|cc| {
134 crate::fonts::install(&cc.egui_ctx);
135 crate::app::BrepApp::new(cc)
136 .map(|mut app| {
137 app.start_report_followup(&cc.egui_ctx);
138 #[cfg(feature = "automation")]
139 crate::web_pump::install(&cc.egui_ctx, cc.wgpu_render_state.clone(), app.automation());
140 // The page's tool surface (WebMCP) answers through
141 // the app's own automation queue: hand it over
142 // here, where the app first exists.
143 #[cfg(feature = "mcp")]
144 crate::webmcp::attach(app.automation().clone(), app.diagnostics().adapter_line());
145 Box::new(app) as Box<dyn eframe::App>
146 })
147 .map_err(|e| e.into())
148 }),
149 )
150 .await
151 }
152}