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

+43
View File
@@ -1147,6 +1147,49 @@ impl OpenCADStudio {
)
}
}
// Web: no filesystem path; the drawing comes back in the reply.
#[cfg(target_arch = "wasm32")]
"save" => {
use base64::Engine as _;
let default_is_dxf = crate::io::source_is_dxf(None, &self.tabs[i].scene.document);
let (version, is_dxf) = super::automation::requested_save_target(
req,
self.tabs[i].scene.document.version,
default_is_dxf,
None,
)
.map_err(|error| failure("invalid_target", error))?;
let dropped =
crate::io::dropped_on_save_count(&self.tabs[i].scene.document, version, is_dxf);
if dropped > 0 && req["allow_lossy"].as_bool() != Some(true) {
return Err(failure(
"lossy_conversion_not_acknowledged",
format!(
"Conversion would drop {dropped} unsupported record(s); set allow_lossy=true to acknowledge"
),
));
}
let ext = if is_dxf { "dxf" } else { "dwg" };
self.sync_view_state_for_save(i);
super::update::util::sync_annotation_scale_header(&mut self.tabs[i].scene);
self.stamp_header_sysvars(i);
self.sync_solid_models_for_save(i);
self.tabs[i].scene.sync_native_parametric_graph();
let bytes = crate::io::save_to_bytes(&self.tabs[i].scene.document, ext, version)
.map_err(|error| failure("save_failed", error))?;
self.tabs[i].dirty = false;
let name = req["name"].as_str().filter(|name| !name.is_empty());
let name = name.map_or_else(|| format!("drawing.{ext}"), str::to_string);
self.set_control_result(json!({
"name": name,
"target_format": ext,
"target_version": format!("{version:?}"),
"dropped_on_save": dropped,
"bytes": bytes.len(),
"data_base64": base64::engine::general_purpose::STANDARD.encode(&bytes),
}));
Task::none()
}
#[cfg(not(target_arch = "wasm32"))]
"save_verified" => {
let result = self.save_verified_request(req)?;