Skip to main content

konjure_wasm/
lib.rs

1//! Batch browser exports. All authoring, geometry and behavior use `konjure-sdk`.
2use konjure_sdk::{Diagnostic, Event, Runtime, SceneDocument};
3use wasm_bindgen::prelude::*;
4
5mod language;
6pub use language::*;
7mod browser;
8pub use browser::*;
9
10const MAX_DOCUMENT_BYTES: usize = 1024 * 1024;
11const FRAME_STRIDE: usize = 20;
12
13fn diagnostic(error: Diagnostic) -> JsValue {
14    JsValue::from_str(&serde_json::to_string(&error).unwrap_or_else(|_| error.to_string()))
15}
16fn json_error(error: impl std::fmt::Display) -> JsValue {
17    JsValue::from_str(&format!("{error}"))
18}
19fn encode(value: &impl serde::Serialize) -> Result<String, JsValue> {
20    serde_json::to_string(value).map_err(json_error)
21}
22
23/// Compile a source document without allocating a renderer or requesting permissions.
24#[wasm_bindgen]
25pub fn compile(source: &str) -> Result<String, JsValue> {
26    encode(&konjure_sdk::compile(source).map_err(diagnostic)?)
27}
28
29#[wasm_bindgen]
30pub fn sdk_version() -> String {
31    env!("CARGO_PKG_VERSION").into()
32}
33
34/// Opaque runtime; consumers must call `.free()` when disposing the view.
35#[wasm_bindgen]
36pub struct SdkRuntime {
37    inner: Runtime,
38}
39
40#[wasm_bindgen]
41impl SdkRuntime {
42    #[wasm_bindgen(constructor)]
43    pub fn new(source: &str) -> Result<SdkRuntime, JsValue> {
44        let scene = konjure_sdk::compile(source).map_err(diagnostic)?;
45        Ok(Self {
46            inner: Runtime::new(scene).map_err(diagnostic)?,
47        })
48    }
49
50    #[wasm_bindgen(js_name = fromDocument)]
51    pub fn from_document(document: &str) -> Result<SdkRuntime, JsValue> {
52        if document.len() > MAX_DOCUMENT_BYTES {
53            return Err(JsValue::from_str("document exceeds 1 MiB"));
54        }
55        let scene: SceneDocument = serde_json::from_str(document).map_err(json_error)?;
56        Ok(Self {
57            inner: Runtime::new(scene).map_err(diagnostic)?,
58        })
59    }
60
61    pub fn document_json(&self) -> Result<String, JsValue> {
62        encode(self.inner.scene())
63    }
64    pub fn meshes_json(&self) -> Result<String, JsValue> {
65        encode(self.inner.meshes())
66    }
67    pub fn sample_json(&self, time_seconds: f64) -> Result<String, JsValue> {
68        encode(&self.inner.sample(time_seconds).map_err(diagnostic)?)
69    }
70    /// Stable ID order, matching `frame()`. Meshes are keyed by these IDs.
71    pub fn ids_json(&self) -> Result<String, JsValue> {
72        encode(
73            &self
74                .inner
75                .sample(0.0)
76                .map_err(diagnostic)?
77                .draws
78                .iter()
79                .map(|d| &d.entity)
80                .collect::<Vec<_>>(),
81        )
82    }
83    /// One bulk buffer per frame: 16 column-major matrix floats, then RGBA in `[0, 1]`.
84    pub fn frame(&self, time_seconds: f64) -> Result<Vec<f32>, JsValue> {
85        let frame = self.inner.sample(time_seconds).map_err(diagnostic)?;
86        let mut packed = Vec::with_capacity(frame.draws.len() * FRAME_STRIDE);
87        for draw in frame.draws {
88            for value in draw.matrix {
89                let value = value as f32;
90                if !value.is_finite() {
91                    return Err(JsValue::from_str(
92                        "world transform exceeds the render float range",
93                    ));
94                }
95                packed.push(value);
96            }
97            packed.extend(
98                [draw.color.r, draw.color.g, draw.color.b, draw.color.a]
99                    .map(|v| f32::from(v) / 255.0),
100            );
101        }
102        Ok(packed)
103    }
104    pub fn dispatch(&mut self, event: &str) -> Result<String, JsValue> {
105        if event.len() > 4096 {
106            return Err(JsValue::from_str("event exceeds 4 KiB"));
107        }
108        let event: Event = serde_json::from_str(event).map_err(json_error)?;
109        encode(&self.inner.dispatch(event).map_err(diagnostic)?)
110    }
111}