Skip to content

Commit ba8e927

Browse files
committed
Narrow drawio saves to server-side save-as flow
1 parent b89690c commit ba8e927

File tree

2 files changed

+302
-16
lines changed

2 files changed

+302
-16
lines changed

runtime/extensions/viewers/drawio-editor/index.ts

Lines changed: 152 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ if (readOnly && readonlyLock) readonlyLock.classList.add('active');
205205
var DEFAULT_DRAWIO_XML = ${JSON.stringify(DEFAULT_DRAWIO_XML)};
206206
var xmlData = DEFAULT_DRAWIO_XML;
207207
var modified = false;
208+
var pendingServerSaveAs = null;
208209
var 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
237238
function 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

Comments
 (0)