feat(web): return saved drawings to an embedding page

The web control channel gains a save operation that serializes the
active drawing in the requested format and returns it as base64 in the
reply, clearing the dirty flag, the counterpart of opening from bytes.
A host page can also define window.openCadStudioSaveFile(name, bytes)
to receive every saved or exported file in place of a browser
download; while it is set no file picker opens.

Fixes #1704
Fixes #1705

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-13 10:12:07 +03:00
1 parent d1a2e9db64
commit 85627ebc8a
4 files changed
+91 -2

No files matched your search

+18 -1
View File
@@ -597,10 +597,15 @@ pub fn download_bytes(name: &str, bytes: &[u8]) {
let Some(window) = web_sys::window() else {
return;
};
let array = js_sys::Uint8Array::from(bytes);
// A host page that embeds the app takes the file instead of a download.
if let Some(hook) = save_hook() {
let _ = hook.call2(&window, &wasm_bindgen::JsValue::from_str(name), &array);
return;
}
let Some(document) = window.document() else {
return;
};
let array = js_sys::Uint8Array::from(bytes);
let parts = js_sys::Array::new();
parts.push(&array.buffer());
let Ok(blob) = web_sys::Blob::new_with_u8_array_sequence(&parts) else {
@@ -618,6 +623,18 @@ pub fn download_bytes(name: &str, bytes: &[u8]) {
let _ = web_sys::Url::revoke_object_url(&url);
}
/// The `window.openCadStudioSaveFile(name, bytes)` function an embedding page
/// registers to receive saved and exported files, if any.
#[cfg(target_arch = "wasm32")]
pub fn save_hook() -> Option<js_sys::Function> {
use wasm_bindgen::JsCast;
let window = web_sys::window()?;
js_sys::Reflect::get(window.as_ref(), &wasm_bindgen::JsValue::from_str("openCadStudioSaveFile"))
.ok()?
.dyn_into::<js_sys::Function>()
.ok()
}
/// Show a plot preview as an in-page overlay. The plotted PDF arrives already
/// rasterised to PNG pages: inline `<img>` elements render in every browser,
/// with no popup permission and no PDF viewer plugin — both of which embedded