CMMS embedding: load-from-URL on boot, POST-save instead of download

Additive only, gated entirely on window.MESH_TOOL_CONFIG being present (set
by the CMMS's public/mesh-edit.php host page, absent everywhere else -
standalone use is unaffected).

- src/main/mesh.js: cmms_load_from_url() fetches bytes from the
  host-provided loadUrl and feeds them through the existing load_files()
  path (same one drag-and-drop and the Import button use) - skips
  restore_space() when embedded, since job-scoped launch means "load this
  exact file", not whatever was last open in this browser's IndexedDB.
- src/mesh/build.js: util.download() calls cmms_save_to_host() instead of
  triggering a browser download when embedded - POSTs to the same
  job/manufacturing_project-scoped save endpoint pattern as the CAD editor
  fork's post_bytes_to_host().
This commit is contained in:
AI Assistant 2026-08-23 17:17:33 +10:00
commit ee5b863f02
2 changed files with 62 additions and 2 deletions

View file

@ -150,8 +150,19 @@ function init() {
// trigger space event binding
call.space_init({ space: space, platform });
// reload stored space when worker is ready
motoClient.on('ready', restore_space);
// CMMS embedding (not upstream): window.MESH_TOOL_CONFIG is set by the
// host page (public/mesh-edit.php) when this is launched job-scoped to
// repair one specific attachment, rather than the normal freestanding
// app. When present, load that file instead of restoring whatever
// workspace happened to be in this browser's local IndexedDB - the
// whole point of a job-scoped launch is "this exact file", not
// whatever was last open here.
if (self.MESH_TOOL_CONFIG && self.MESH_TOOL_CONFIG.loadUrl) {
motoClient.on('ready', () => cmms_load_from_url(self.MESH_TOOL_CONFIG));
} else {
// reload stored space when worker is ready
motoClient.on('ready', restore_space);
}
// start worker
motoClient.start('../lib/mesh/work.js?' + version);
@ -735,6 +746,23 @@ function space_init(data) {
});
}
// CMMS embedding (not upstream): fetch bytes from the host-provided,
// already-validated loadUrl and feed them through the exact same
// load_files() path drag-and-drop/the Import button already use - a
// fetched Blob wrapped in a File object looks identical to one the user
// picked, so no new parsing/import logic is needed here at all.
async function cmms_load_from_url(config) {
const response = await fetch(config.loadUrl);
if (!response.ok) {
log(`CMMS load failed: HTTP ${response.status}`).pin({});
return;
}
const buf = await response.arrayBuffer();
const name = config.originalName || 'attachment.stl';
const file = new File([buf], name, { type: 'application/octet-stream' });
load_files([file]);
}
function load_files(files) {
log(`loading file...`);
let has_image = false;

View file

@ -29,11 +29,43 @@ let spin_timer;
// add download / blob export to util
util.download = (data, filename = "mesh-data") => {
// CMMS embedding (not upstream): when launched from mesh-edit.php,
// Export POSTs the result back to the CMMS instead of triggering a
// browser download. Standalone use (no window.MESH_TOOL_CONFIG) is
// unaffected - same pattern as the CAD editor fork's download_bytes
// override.
if (self.MESH_TOOL_CONFIG) {
cmms_save_to_host(data, filename);
return;
}
let url = window.URL.createObjectURL(new Blob([data], {type: "octet/stream"}));
$('download').innerHTML = `<a id="_data_export_" href="${url}" download="${filename}">x</a>`;
$('_data_export_').click();
};
// CMMS embedding (not upstream): POST exported bytes to the job/
// manufacturing_project-scoped save endpoint (mirrors the CAD editor
// fork's post_bytes_to_host). MESH_TOOL_CONFIG's fields are the same
// shape mesh-edit.php emits: saveUrl, ownerToken ("job:123" or
// "manufacturing_project:45"), csrfToken.
async function cmms_save_to_host(data, filename) {
const cfg = self.MESH_TOOL_CONFIG;
const form = new FormData();
form.append('action', 'mesh_save_new');
form.append('job_id', cfg.ownerToken);
form.append('original_name', filename);
form.append('csrf_token', cfg.csrfToken);
form.append('file', new Blob([data]), filename);
try {
const res = await fetch(cfg.saveUrl, { method: 'POST', body: form });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json();
log(`saved to CMMS as attachment #${body.id}`);
} catch (err) {
log(`CMMS save failed: ${err.message}`).pin({});
}
}
// add modal dialog functions to api
const modal = api.modal = {
show(title, contents) {