//! In-browser **deterministic byte-exact generation** — the create-side of the //! integer contract, zero JS ML runtime. //! //! The same pure-i64 Rust that generates natively compiles straight to wasm32 and //! produces the IDENTICAL bytes here: a generated image whose hash is portable, so //! the page can pin "this prompt+seed under this model produces exactly these //! bytes" and verify it client-side. No float, no ONNX — `wai.generate.spectral`. use wasm_bindgen::prelude::*; /// A generated image: RGB row-major, `width * height * 3` bytes. #[wasm_bindgen] pub struct GeneratedImage { rgb: Vec, width: u32, height: u32, } #[wasm_bindgen] impl GeneratedImage { #[wasm_bindgen(getter)] pub fn rgb(&self) -> Box<[u8]> { self.rgb.clone().into_boxed_slice() } #[wasm_bindgen(getter)] pub fn width(&self) -> u32 { self.width } #[wasm_bindgen(getter)] pub fn height(&self) -> u32 { self.height } } /// Generate an image deterministically from `(model, prompt, seed, steps)`, in the /// browser, integer-exact. `model_json` is the pinned model bundle /// (`generate_spectral_model.json`); the output is byte-identical to the native /// reference, so its FNV-1a is portable. No float, no ML runtime. #[wasm_bindgen(js_name = generateSpectral)] pub fn generate_spectral( model_json: &[u8], prompt: &str, seed: u32, steps: u32, w: u32, h: u32, ) -> Result { let v: serde_json::Value = serde_json::from_slice(model_json).map_err(|e| JsError::new(&e.to_string()))?; let m = wai::generate::SpectralModel::from_value(&v); let rgb = wai::generate::generate(&m, prompt, seed as u64, steps as usize, h as usize, w as usize); Ok(GeneratedImage { rgb, width: w, height: h }) } /// FNV-1a 64-bit of `bytes` as lowercase hex — the portable generation hash the /// page pins and re-checks, the same checksum the native generator emits. #[wasm_bindgen(js_name = fnv1aHex)] pub fn fnv1a_hex(bytes: &[u8]) -> String { format!("{:016x}", wai::generate::fnv1a(bytes)) }