Implement animation export

This commit is contained in:
Keavon Chambers
2026-05-17 15:29:09 -04:00
parent 21d2994059
commit 1d3d32c169
18 changed files with 541 additions and 58 deletions
+1
View File
@@ -39,6 +39,7 @@ web-sys = { workspace = true }
ron = { workspace = true }
serde_json = { workspace = true }
node-macro = { workspace = true }
zip = { workspace = true }
[package.metadata.wasm-pack.profile.dev]
wasm-opt = false
+34
View File
@@ -974,6 +974,40 @@ impl EditorWrapper {
// Static functions callable from JavaScript without an Editor instance
// ====================================================================
/// Build an uncompressed (store-only) ZIP archive from a list of `[filename, bytes]` entries.
///
/// Used by the animation export flow on the web build: web APIs cannot offer a multi-file save,
/// so the frontend packs all frames into a single `.zip` to download. On desktop, individual files
/// are written to a user-chosen folder instead and this function is unused.
#[wasm_bindgen(js_name = createZipFromFiles)]
pub fn create_zip_from_files(entries: js_sys::Array) -> Result<Vec<u8>, JsValue> {
use std::io::{Cursor, Write};
use zip::write::{SimpleFileOptions, ZipWriter};
let mut buffer = Cursor::new(Vec::<u8>::new());
let mut writer = ZipWriter::new(&mut buffer);
// Skip compression since raster/SVG payloads are already small or already compressed
let options: SimpleFileOptions = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored).unix_permissions(0o644);
for entry in entries.iter() {
let pair = entry
.dyn_ref::<js_sys::Array>()
.ok_or_else(|| JsValue::from_str("createZipFromFiles: each entry must be a [filename, bytes] array"))?;
let filename = pair.get(0).as_string().ok_or_else(|| JsValue::from_str("createZipFromFiles: filename must be a string"))?;
let bytes = js_sys::Uint8Array::new(&pair.get(1)).to_vec();
writer
.start_file(filename, options)
.map_err(|e| JsValue::from_str(&format!("createZipFromFiles: start_file failed: {e}")))?;
writer.write_all(&bytes).map_err(|e| JsValue::from_str(&format!("createZipFromFiles: write_all failed: {e}")))?;
}
writer.finish().map_err(|e| JsValue::from_str(&format!("createZipFromFiles: finish failed: {e}")))?;
Ok(buffer.into_inner())
}
#[wasm_bindgen(js_name = evaluateMathExpression)]
pub fn evaluate_math_expression(expression: &str) -> Option<f64> {
let value = math_parser::evaluate(expression)