@@ -205,6 +205,7 @@ if (readOnly && readonlyLock) readonlyLock.classList.add('active');
205205var DEFAULT_DRAWIO_XML = ${ JSON . stringify ( DEFAULT_DRAWIO_XML ) } ;
206206var xmlData = DEFAULT_DRAWIO_XML;
207207var modified = false;
208+ var pendingServerSaveAs = null;
208209var format = (function() {
209210 var lower = String(sourceName || '').toLowerCase();
210211 if (lower.endsWith('.drawio.svg') || lower.endsWith('.svg')) return 'xmlsvg';
@@ -236,18 +237,61 @@ function responseToDataUri(response, fallbackMimeType) {
236237
237238function patchDrawioExportTarget(win) {
238239 try {
239- function postExport( payload) {
240+ function postParentEvent(eventName, payload) {
240241 var target = (win && (win.parent || win.opener)) || window;
241- target.postMessage(JSON.stringify(Object.assign({ event: 'workspace-export' }, payload)), '*');
242+ target.postMessage(JSON.stringify(Object.assign({ event: eventName }, payload)), '*');
242243 return true;
243244 }
245+ function postExport(payload) {
246+ return postParentEvent('workspace-export', payload);
247+ }
248+ function resolveBaseName(filename) {
249+ var text = String(filename || 'diagram.drawio').trim() || 'diagram.drawio';
250+ return text
251+ .replace(/\.drawio\.(svg|png|xml)$/i, '')
252+ .replace(/\.(svg|png|jpe?g|pdf|xml)$/i, '')
253+ .replace(/\.drawio$/i, '') || 'diagram';
254+ }
255+ function mimeTypeForFormat(targetFormat) {
256+ switch (targetFormat) {
257+ case 'svg': return 'image/svg+xml';
258+ case 'png': return 'image/png';
259+ case 'jpeg':
260+ case 'jpg': return 'image/jpeg';
261+ default: return 'application/xml';
262+ }
263+ }
264+ function extensionForFormat(targetFormat) {
265+ switch (targetFormat) {
266+ case 'svg': return '.svg';
267+ case 'png': return '.png';
268+ case 'jpeg':
269+ case 'jpg': return '.jpg';
270+ default: return '.drawio';
271+ }
272+ }
273+ function findEditorUi() {
274+ try {
275+ var keys = Object.keys(win || {});
276+ for (var i = 0; i < keys.length; i++) {
277+ var value = win[keys[i]];
278+ if (!value || typeof value !== 'object') continue;
279+ if (typeof value.saveFile === 'function' && value.actions && value.menus && value.editor) {
280+ return value;
281+ }
282+ }
283+ } catch (_) {
284+ // ignore transient globals while draw.io is still booting
285+ }
286+ return null;
287+ }
244288
245289 var editorUiCtor = win && win.EditorUi;
246290 if (editorUiCtor && editorUiCtor.prototype && !editorUiCtor.prototype.__piclawWorkspaceSavePatched) {
247291 var originalSaveData = editorUiCtor.prototype.saveData;
248292 editorUiCtor.prototype.saveData = function(filename, format, data, mime, base64Encoded, defaultMode) {
249293 try {
250- if (filename && data != null && postExport({ filename, format, data, mimeType: mime, base64Encoded: !!base64Encoded, defaultMode: defaultMode })) {
294+ if (filename && data != null && postExport({ filename, format, data, xml: mime === 'application/xml' || mime === 'text/xml' ? data : undefined, mimeType: mime, base64Encoded: !!base64Encoded, defaultMode: defaultMode })) {
251295 return;
252296 }
253297 } catch (err) {
@@ -274,7 +318,67 @@ function patchDrawioExportTarget(win) {
274318 appCtor.prototype.__piclawExportPatched = true;
275319 }
276320
277- return !!((editorUiCtor && editorUiCtor.prototype && editorUiCtor.prototype.__piclawWorkspaceSavePatched) || (appCtor && appCtor.prototype && appCtor.prototype.__piclawExportPatched));
321+ var ui = findEditorUi();
322+ if (ui && !ui.__piclawServerSaveFlowPatched) {
323+ var saveAsAction = ui.actions && (ui.actions.get('saveAs') || ui.actions.get('saveAs...'));
324+ if (saveAsAction) {
325+ var originalSaveAs = saveAsAction.funct;
326+ saveAsAction.funct = function() {
327+ try {
328+ var currentFilename = String((ui.getCurrentFile && ui.getCurrentFile().getTitle && ui.getCurrentFile().getTitle()) || (ui.getCurrentFile && ui.getCurrentFile().title) || 'diagram.drawio');
329+ var choice = String((win.prompt && win.prompt('Save on server as: drawio, svg, png, jpeg', 'drawio')) || '').trim().toLowerCase();
330+ if (!choice) return;
331+ var normalized = choice === 'jpg' ? 'jpeg' : choice;
332+ if (['drawio', 'xml', 'svg', 'png', 'jpeg'].indexOf(normalized) === -1) {
333+ win.alert && win.alert('Supported server-side save formats: drawio, svg, png, jpeg');
334+ return;
335+ }
336+ var xml = typeof ui.getFileData === 'function' ? ui.getFileData(true) : null;
337+ if (typeof xml !== 'string' || !xml.trim()) {
338+ win.alert && win.alert('Could not read the current diagram XML for Save As.');
339+ return;
340+ }
341+ var targetFormat = normalized === 'xml' ? 'drawio' : normalized;
342+ var filename = resolveBaseName(currentFilename) + extensionForFormat(targetFormat);
343+ postParentEvent('workspace-save-as', {
344+ filename: filename,
345+ format: targetFormat,
346+ xml: xml,
347+ mimeType: mimeTypeForFormat(targetFormat)
348+ });
349+ } catch (err) {
350+ console.warn('[drawio] custom saveAs failed, falling back to native action', err);
351+ return originalSaveAs && originalSaveAs.apply(saveAsAction, []);
352+ }
353+ };
354+ }
355+
356+ var exportAction = ui.actions && ui.actions.get('export');
357+ if (exportAction) {
358+ exportAction.setEnabled && exportAction.setEnabled(false);
359+ exportAction.isEnabled = function() { return false; };
360+ exportAction.funct = function() {
361+ win.alert && win.alert('Use File → Save As… to save SVG, PNG, JPEG, or Draw.io to the server.');
362+ };
363+ }
364+
365+ var exportAsMenu = ui.menus && ui.menus.get('exportAs');
366+ if (exportAsMenu) {
367+ exportAsMenu.setEnabled && exportAsMenu.setEnabled(false);
368+ exportAsMenu.isEnabled = function() { return false; };
369+ }
370+
371+ var fileMenu = ui.menus && ui.menus.get('file');
372+ if (fileMenu) {
373+ fileMenu.funct = function(menu, parent) {
374+ ui.menus.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import', '-', 'pageSetup', 'print'], parent);
375+ };
376+ }
377+
378+ ui.__piclawServerSaveFlowPatched = true;
379+ }
380+
381+ return !!((editorUiCtor && editorUiCtor.prototype && editorUiCtor.prototype.__piclawWorkspaceSavePatched) || (appCtor && appCtor.prototype && appCtor.prototype.__piclawExportPatched) || (ui && ui.__piclawServerSaveFlowPatched));
278382 } catch (_) {
279383 return false;
280384 }
@@ -407,21 +511,61 @@ window.addEventListener('message', function(e) {
407511 }
408512 break;
409513
514+ case 'workspace-save-as':
515+ if (readOnly || !filePath) break;
516+ if (msg.format === 'drawio' || msg.format === 'xml') {
517+ if (!msg.xml) break;
518+ saveWorkspace({
519+ xml: msg.xml,
520+ data: msg.xml,
521+ format: 'xml',
522+ mimeType: msg.mimeType || 'application/xml',
523+ filename: msg.filename
524+ }, true).catch(function(err) {
525+ console.error('[drawio] save-as xml error:', err);
526+ });
527+ break;
528+ }
529+ pendingServerSaveAs = {
530+ filename: msg.filename,
531+ mimeType: msg.mimeType,
532+ format: msg.format
533+ };
534+ if (msg.xml && frame.contentWindow) {
535+ frame.contentWindow.postMessage(JSON.stringify({
536+ action: 'export',
537+ format: msg.format,
538+ xml: msg.xml,
539+ spinKey: 'export'
540+ }), '*');
541+ }
542+ break;
543+
410544 case 'export':
411545 if (readOnly || !filePath || !msg.data) break;
412- saveWorkspace({ data: msg.data, format: format, xml: msg.xml }, true).catch(function(err) {
546+ var pendingSaveAs = pendingServerSaveAs;
547+ pendingServerSaveAs = null;
548+ saveWorkspace({
549+ data: msg.data,
550+ format: (pendingSaveAs && pendingSaveAs.format) || format,
551+ xml: msg.xml,
552+ mimeType: pendingSaveAs && pendingSaveAs.mimeType,
553+ filename: pendingSaveAs && pendingSaveAs.filename
554+ }, true).catch(function(err) {
413555 console.error('[drawio] export save error:', err);
414556 });
415557 break;
416558
417559 case 'workspace-export':
418560 if (readOnly || !filePath || !msg.data) break;
561+ var pendingWorkspaceSaveAs = pendingServerSaveAs;
562+ pendingServerSaveAs = null;
419563 saveWorkspace({
420564 data: msg.data,
421565 xml: msg.xml,
422- format: format,
423- mimeType: msg.mimeType,
424- filename: msg.filename,
566+ format: (pendingWorkspaceSaveAs && pendingWorkspaceSaveAs.format) || format,
567+ mimeType: (pendingWorkspaceSaveAs && pendingWorkspaceSaveAs.mimeType) || msg.mimeType,
568+ filename: (pendingWorkspaceSaveAs && pendingWorkspaceSaveAs.filename) || msg.filename,
425569 base64Encoded: !!msg.base64Encoded
426570 }, true).catch(function(err) {
427571 console.error('[drawio] workspace export save error:', err);
0 commit comments