clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

render diagrams.net with official client exporter

Dami committed 64e87f4
commit
64e87f40e85b1275a1c55cba565f4750f1a830c0
parent
cc0879d014965f55093ecc7c630154ddd120f9e8
author
Dami <dami.etoile@gmail.com>
19 files changed, +1292 -218
A.gitattributes7+
Mdiagrams.net/README.md9+
Mdiagrams.net/assets/js/export-init.js4+
Mdiagrams.net/assets/js/export.js816++++++++++++++++++++++--------
Mdiagrams.net/import.sh2+
Adiagramzip-client-units/diagramsnet/.gitignore3+
Adiagramzip-client-units/diagramsnet/package-lock.json499++++++++++++++++++
Adiagramzip-client-units/diagramsnet/package.json12+
Adiagramzip-client-units/diagramsnet/scripts/build.mjs19+
Adiagramzip-client-units/diagramsnet/scripts/check.mjs16+
Adiagramzip-client-units/diagramsnet/src/frame.js50++
Adiagramzip-client-units/diagramsnet/unit/_headers4+
Adiagramzip-client-units/diagramsnet/unit/index.html14+
Adiagramzip-client-units/diagramsnet/unit/worker.js32+
Adiagramzip-client-units/diagramsnet/wrangler.jsonc15+
Mdiagramzip/src/client-renderers.js1+
Mdiagramzip/test/client-renderers.test.js3+-
Mdocs/refactor-engines.md2+-
Mserver/src/main/resources/web/diagramzip/assets/index-CzBQjn0p.js2+-
A .gitattributes +7 -0
M diagrams.net/README.md +9 -0
M diagrams.net/assets/js/export-init.js +4 -0
M diagrams.net/assets/js/export.js +601 -215
@@ -1,13 +1,174 @@
1+/**2+ * Copyright (c) 2020-2025, JGraph Holdings Ltd3+ * Copyright (c) 2020-2025, draw.io AG4+ */15 var mxIsElectron = navigator.userAgent != null &&2- navigator.userAgent.toLowerCase().indexOf(' electron/') > -1;6+ navigator.userAgent.toLowerCase().indexOf(' electron/') > -1 && 7+ navigator.userAgent.indexOf(' draw.io/') > -1;38 var GOOGLE_APPS_MAX_AREA = 25000000;4-var GOOGLE_SHEET_MAX_AREA = 1048576; //1024x10249+var GOOGLE_SHEET_MAX_AREA = 1000000; // The maximum number of pixels is 1 million.10+var shadowBlocker = null;511 6-//TODO Add support for loading math from a local folder7-Editor.initMath((remoteMath? 'https://app.diagrams.net/' : '') + 'math/MathJax.js');12+/**13+ * Adds shadow blocker style.14+ */15+function addShadowBlocker()16+{17+ if (shadowBlocker == null)18+ {19+ shadowBlocker = document.createElement('style');20+ shadowBlocker.setAttribute('type', 'text/css');21+ shadowBlocker.innerHTML = '@media print {\n' +22+ ' g[style*="filter: drop-shadow("] {\n' +23+ ' filter: none !important;\n' +24+ ' }\n' +25+ '}\n';26+ 27+ var head = document.getElementsByTagName('head')[0];28+ 29+ if (head != null)30+ {31+ head.appendChild(shadowBlocker);32+ }33+ }34+}35+36+/**37+ * Adds meta tag to the page.38+ */39+function mxmeta(content, httpEquiv)40+{41+ try42+ {43+ var s = document.createElement('meta');44+ 45+ s.setAttribute('content', content);46+ s.setAttribute('http-equiv', httpEquiv);47+48+ var t = document.getElementsByTagName('meta')[0];49+ t.parentNode.insertBefore(s, t);50+ }51+ catch (e)52+ {53+ // ignore54+ }55+};56+57+function mxscript(src, onLoad)58+{59+ var s = document.createElement('script');60+ s.setAttribute('type', 'text/javascript');61+ s.setAttribute('src', src);62+ 63+ if (onLoad != null)64+ {65+ var r = false;66+ 67+ s.onload = s.onreadystatechange = function()68+ {69+ if (!r && (!this.readyState || this.readyState == 'complete'))70+ {71+ r = true;72+ onLoad();73+ }74+ };75+ }76+77+ var t = document.getElementsByTagName('script')[0];78+ 79+ if (t != null)80+ {81+ t.parentNode.insertBefore(s, t);82+ }83+};84+85+if (mxIsElectron)86+{87+ mxmeta('default-src \'self\'; script-src \'self\'; connect-src \'self\' https://*.draw.io https://*.diagrams.net https://fonts.googleapis.com ' +88+ 'https://fonts.gstatic.com; img-src * data:; media-src *; font-src * data:; frame-src \'none\'; style-src \'self\' \'unsafe-inline\' ' +89+ 'https://fonts.googleapis.com; base-uri \'none\';child-src \'self\';object-src \'none\';', 'Content-Security-Policy');90+ 91+ // We can't use eval in Electron because of CSP, so load all shapes and disable eval92+ mxscript('js/stencils.min.js', function()93+ {94+ mxscript('js/shapes-14-6-5.min.js', function()95+ {96+ if (window.pendingRequest != null)97+ {98+ render(window.pendingRequest);99+ }100+101+ window.shapesLoaded = true;102+ });103+ });104+ 105+ // Disables eval for JS (uses shapes-14-6-5.min.js)106+ mxStencilRegistry.allowEval = false;107+}108+109+// TODO Add support for loading math from a local folder110+Editor.initMath((remoteMath? 'https://app.diagrams.net/' : '') + 'math4/es5/startup.js');111+112+// Marks individual font and CSS URLs as preloaded113+var fontPreload = {};114+var cssPreload = {};8115 9116 function render(data)10117 {118+ if (data.shadows == '0')119+ {120+ addShadowBlocker();121+ }122+ 123+ if (data.csv != null)124+ {125+ // CSV loads orgChart asynchronously and needs mxscript126+ window.mxscript = function (src, onLoad, id)127+ {128+ var s = document.createElement('script');129+ s.setAttribute('type', 'text/javascript');130+ s.setAttribute('defer', 'true');131+ s.setAttribute('src', src);132+133+ if (id != null)134+ {135+ s.setAttribute('id', id);136+ }137+ 138+ if (onLoad != null)139+ {140+ var r = false;141+ 142+ s.onload = s.onreadystatechange = function()143+ {144+ if (!r && (!this.readyState || this.readyState == 'complete'))145+ {146+ r = true;147+ onLoad();148+ }149+ };150+ }151+ 152+ var t = document.getElementsByTagName('script')[0];153+ 154+ if (t != null)155+ {156+ t.parentNode.insertBefore(s, t);157+ }158+ };159+160+ var editorUi = new HeadlessEditorUi();161+ 162+ editorUi.importCsv(data.csv, function()163+ {164+ data.xml = mxUtils.getXml(editorUi.editor.getGraphXml());165+ delete data.csv;166+ render(data);167+ });168+169+ return;170+ }171+11172 var autoScale = false;12173 13174 if (data.scale == 'auto')
@@ -16,7 +177,7 @@ function render(data)
16177 data.scale = 1;17178 }18179 19- document.body.innerHTML = '';180+ document.body.innerText = '';20181 var container = document.createElement('div');21182 container.id = 'graph';22183 container.style.width = '100%';
@@ -24,6 +185,8 @@ function render(data)
24185 document.body.appendChild(container);25186 26187 var graph = new Graph(container);188+ graph.enableFlowAnimation = true;189+ 27190 data.border = parseInt(data.border) || 0;28191 data.w = parseFloat(data.w) || 0;29192 data.h = parseFloat(data.h) || 0;
@@ -48,7 +211,7 @@ function render(data)
48211 }49212 50213 var gridColor = null;51- 214+52215 if (extras != null && extras.grid != null)53216 {54217 graph.gridSize = extras.grid.size;
@@ -61,14 +224,46 @@ function render(data)
61224 Graph.diagramLanguage = extras.diagramLanguage;62225 Graph.translateDiagram = true;63226 }227+228+ if (data.fileTitle != null)229+ {230+ document.title = data.fileTitle;231+ }232+ else if (extras != null && extras.globalVars != null &&233+ extras.globalVars.filename != null)234+ {235+ document.title = extras.globalVars.filename;236+ }237+ 238+ // Overrides graph bounds to include background images239+ var graphGetGraphBounds = graph.getGraphBounds;240+241+ graph.getGraphBounds = function()242+ {243+ var bounds = graphGetGraphBounds.apply(this, arguments);244+ var img = this.backgroundImage;245+ 246+ if (img != null && img.width != null && img.height != null)247+ {248+ var t = this.view.translate;249+ var s = this.view.scale;250+251+ bounds = mxRectangle.fromRectangle(bounds);252+ bounds.add(new mxRectangle(253+ (t.x + img.x) * s, (t.y + img.y) * s,254+ img.width * s, img.height * s));255+ }256+257+ return bounds;258+ };64259 65- //PNG+XML format260+ // PNG+XML format66261 if (data.xml.substring(0, 5) == 'iVBOR' || (extras != null && extras.isPng))67262 {68263 data.xml = Editor.extractGraphModelFromPng('data:image/png;base64,' + data.xml);69264 }70265 71- //IE11 sends incorrect xml266+ // IE11 sends incorrect xml72267 if (data.xml.substring(0, 11) == '<#document>')73268 {74269 data.xml = data.xml.substring(11, data.xml.length - 12);
@@ -101,10 +296,7 @@ function render(data)
101296 function getFileXml(uncompressed)102297 {103298 var xml = mxUtils.getXml(origXmlDoc);104- EditorUi.prototype.createUi = function(){};105- EditorUi.prototype.addTrees = function(){};106- EditorUi.prototype.updateActionStates = function(){};107- var editorUi = new EditorUi();299+ var editorUi = new HeadlessEditorUi();108300 var tmpFile = new LocalFile(editorUi, xml);109301 editorUi.setCurrentFile(tmpFile);110302 editorUi.setFileData(xml);
@@ -137,6 +329,14 @@ function render(data)
137329 graph.globalVars = extras.globalVars;138330 }139331 332+ /**333+ * Disables custom links but allows page links.334+ */335+ function isLinkIgnored(graph, link)336+ {337+ return link == null || (graph.isCustomLink(link) && !Graph.isPageLink(link));338+ };339+140340 /**141341 * Disables custom links on shapes.142342 */
@@ -145,8 +345,8 @@ function render(data)
145345 graph.getLinkForCell = function(cell)146346 {147347 var link = graphGetLinkForCell.apply(this, arguments);148- 149- if (link != null && this.isCustomLink(link))348+349+ if (isLinkIgnored(this, link))150350 {151351 link = null;152352 }
@@ -171,7 +371,7 @@ function render(data)
171371 {172372 var href = links[i].getAttribute('href');173373 174- if (href != null && graph.isCustomLink(href))374+ if (isLinkIgnored(graph, href))175375 {176376 links[i].setAttribute('href', '#');177377 }
@@ -194,9 +394,12 @@ function render(data)
194394 if (--waitCounter < 1)195395 {196396 //Note: This code targets Chrome as it is the browser used by export server197- //Ensure that all fonts has been loaded, this promise is never rejected397+ //Ensure that all fonts have been loaded, this promise is never rejected198398 document.fonts.ready.then(function() 199399 {400+ // Rewrite page links401+ Graph.rewritePageLinks(document);402+ 200403 var doneDiv = document.createElement("div");201404 var pageCount = diagrams != null? diagrams.length : 1;202405 doneDiv.id = 'LoadingComplete';
@@ -210,7 +413,7 @@ function render(data)
210413 //Electron pdf export211414 if (mxIsElectron)212415 {213- try 416+ try214417 {215418 electron.registerMsgListener('get-svg-data', (arg) => 216419 {
@@ -222,8 +425,27 @@ function render(data)
222425 {223426 bg = null;224427 }428+429+ var theme = 'auto';430+431+ if (data.theme != null)432+ {433+ theme = data.theme;434+ }225435 226- var svgRoot = graph.getSvg(bg, 1, 0, false, null, true, null, null, null);436+ var linkTarget = null;437+438+ if (data.linkTarget == 'same-win')439+ {440+ linkTarget = '_top';441+ }442+ else if (data.linkTarget == 'new-win')443+ {444+ linkTarget = '_blank';445+ }446+447+ var svgRoot = graph.getSvg(bg, expScale, data.border, false, null,448+ true, null, null, linkTarget, null, null, theme);227449 228450 if (graph.shadowVisible)229451 {
@@ -248,18 +470,67 @@ function render(data)
248470 svgRoot.setAttribute('content', getFileXml());249471 }250472 251- electron.sendMessage('svg-data', Graph.xmlDeclaration + '\n' + ((editable) ? Graph.svgFileComment + '\n' : '') +252- Graph.svgDoctype + '\n' + mxUtils.getXml(svgRoot));473+ electron.sendMessage('svg-data',474+ Graph.xmlDeclaration + '\n' +475+ ((editable) ? Graph.svgFileComment + '\n' : '') +476+ Graph.svgDoctype + '\n' + mxUtils.getXml(svgRoot));253477 };254478 255- if (data.embedImages == '1')479+ function embedFontsDone()480+ {481+ if (data.embedImages == '1')482+ {483+ var tmpEditor = new Editor();484+ tmpEditor.convertImages(svgRoot, doSend);485+ }486+ else487+ {488+ doSend();489+ }490+ }491+492+ if (data.embedFonts == '1')256493 {257- var tmpEditor = new Editor();258- tmpEditor.convertImages(svgRoot, doSend);494+ var extFonts = graph.getCustomFonts();495+ 496+ // Adds external fonts497+ // TODO CSP will not allow external fonts!498+ if (extFonts.length > 0)499+ {500+ var svgDoc = svgRoot.ownerDocument;501+ var style = (svgDoc.createElementNS != null) ?502+ svgDoc.createElementNS(mxConstants.NS_SVG, 'style') : svgDoc.createElement('style');503+ (svgDoc.setAttributeNS != null) ? style.setAttributeNS('type', 'text/css') :504+ style.setAttribute('type', 'text/css');505+ 506+ var prefix = '';507+ var postfix = '';508+ 509+ for (var i = 0; i < extFonts.length; i++)510+ {511+ var fontName = extFonts[i].name, fontUrl = extFonts[i].url;512+ 513+ if (Graph.isCssFontUrl(fontUrl))514+ {515+ prefix += '@import url(' + Graph.rewriteGoogleFontUrl(fontUrl) + ');\n';516+ }517+ else518+ {519+ postfix += '@font-face {\n' +520+ 'font-family: "' + fontName + '";\n' + 521+ 'src: url("' + fontUrl + '");\n}\n';522+ } 523+ }524+ 525+ style.appendChild(svgDoc.createTextNode(prefix + postfix));526+ svgRoot.getElementsByTagName('defs')[0].appendChild(style);527+ }528+529+ EditorUi.prototype.replaceAlternateContent(svgRoot, null, embedFontsDone);259530 }260531 else261532 {262- doSend();533+ embedFontsDone();263534 }264535 });265536
@@ -299,125 +570,157 @@ function render(data)
299570 }300571 };301572 302- // Waits for MathJax.Hub to become available to register303- // wait counter callback asynchronously after math render304- var editorDoMathJaxRender = Editor.doMathJaxRender;573+ // Waits for MathJax autoloading and rendering574+ var editorOnMathJaxDone = Editor.onMathJaxDone;305575 306- Editor.doMathJaxRender = function(container)576+ Editor.onMathJaxDone = function()307577 {308- editorDoMathJaxRender.apply(this, arguments);309- 310- window.setTimeout(function()311- {312- window.MathJax.Hub.Queue(function ()313- {314- decrementWaitCounter();315- });316- }, 0);578+ editorOnMathJaxDone.apply(this, arguments);579+ decrementWaitCounter();317580 };318- 319- // Adds async MathJax rendering task581+582+ // Adds MathJax rendering task320583 function renderMath(elt)321584 {322- if (math && Editor.MathJaxRender != null)585+ if (Editor.MathJaxRender != null)323586 {324587 waitCounter++;325588 Editor.MathJaxRender(elt);326589 }327590 };328591 329- function loadExtFonts(extFonts)592+ // Waits for the given font593+ function waitForFont(url)330594 {331595 try332596 {333- extFonts = extFonts.split('|').map(function(ef)597+ if (url != null && fontPreload[url] == null)334598 {335- var parts = ef.split('^');336- return {name: parts[0], url: parts[1]};337- });599+ waitCounter++;600+ fontPreload[url] = true;601+ mxUtils.get(url, decrementWaitCounter,602+ decrementWaitCounter, false, 10000,603+ decrementWaitCounter);604+ }338605 }339- catch(e)606+ catch (e)340607 {341- //ignore and return!342- return;608+ // ignore font343609 }344- 345- waitCounter += extFonts.length;610+ };346611 347- //Note: This code targets Chrome as it is the browser used by export server348- for (var i = 0; i < extFonts.length; i++)612+ // Waits for the fonts in the given CSS613+ function waitForFonts(fontCss)614+ {615+ var parts = fontCss.split('url(');616+ 617+ for (var i = 1; i < parts.length; i++)349618 {350- if (extFonts[i].url.indexOf(Editor.GOOGLE_FONTS) == 0)619+ try620+ {621+ var idx = parts[i].indexOf(')');622+ var url = Editor.trimCssUrl(parts[i].substring(0, idx));623+ waitForFont(url);624+ }625+ catch (e)351626 {627+ // ignore font css628+ }629+ }630+ };631+632+ // Loads and processes the fonts in the given Google Font URL633+ function processGoogleFontCss(url)634+ {635+ try636+ {637+ if (Graph.isGoogleFontUrl(url) && cssPreload[url] == null)638+ { 639+ cssPreload[url] = true;640+352641 var link = document.createElement('link');353- 354- link.setAttribute('rel', 'stylesheet');355- link.setAttribute('charset', 'UTF-8');356- link.setAttribute('type', 'text/css');357- 358- link.onload = decrementWaitCounter;359- link.onerror = decrementWaitCounter;360- 361- link.setAttribute('href', extFonts[i].url);362- var head = document.getElementsByTagName('head')[0];363- head.appendChild(link);642+ link.setAttribute('rel', 'preload');643+ link.setAttribute('as', 'style');644+ link.setAttribute('href', url);645+ document.getElementsByTagName('head')[0].appendChild(link);646+647+ // Loads the stylesheet to wait for fonts648+ waitCounter++;649+650+ mxUtils.get(url, mxUtils.bind(this, function(req)651+ {652+ try653+ {654+ if (req.getStatus() >= 200 && req.getStatus() <= 299)655+ {656+ waitForFonts(req.getText());657+ }658+659+ decrementWaitCounter();660+ }661+ catch(e)662+ {663+ decrementWaitCounter();664+ }665+ }), decrementWaitCounter, false, 10000, decrementWaitCounter);364666 }365- else667+ }668+ catch (e)669+ {670+ // ignore stylesheet671+ }672+ };673+674+ // Waits for Google Font CSS imports in the given element675+ function waitForGoogleFontImports(elt)676+ {677+ var style = elt.getElementsByTagName('style');678+679+ for (var i = 0; i < style.length; i++)680+ {681+ var parts = style[i].innerHTML.split('@import url(');682+683+ for (var i = 1; i < parts.length; i++)366684 {367- //Relative urls doesn't work368- if (extFonts[i].url.indexOf(PROXY_URL) == 0 && PROXY_URL.indexOf('http') == -1)685+ try369686 {370- var href = window.location.href;371- href = href.substring(0, href.lastIndexOf('/') + 1);372- extFonts[i].url = href + extFonts[i].url;687+ var idx = parts[i].indexOf(')');688+ var url = Editor.trimCssUrl(parts[i].substring(0, idx));689+ processGoogleFontCss(url);373690 }374- 375- var font = new FontFace(extFonts[i].name, 'url(' + extFonts[i].url + ')');376- 377- font.load().then(function(loadedFont)691+ catch (e)378692 {379- document.fonts.add(loadedFont);380- decrementWaitCounter();381- }).catch(decrementWaitCounter);693+ // ignore import694+ }382695 }383696 }384697 };385698 386- function renderGrid()699+ // Intercepts loading of Google Fonts CSS700+ var origCreateFontElement = Graph.createFontElement;701+702+ Graph.createFontElement = function(name, url)387703 {388- if (gridColor == null) return;389- 390- var view = graph.view;391- var gridImage = btoa(unescape(encodeURIComponent(view.createSvgGrid(gridColor))));392- gridImage = 'url(' + 'data:image/svg+xml;base64,' + gridImage + ')';393- var phase = graph.gridSize * view.gridSteps * view.scale;394- 395- var x0 = 0;396- var y0 = 0;397- 398- if (view.backgroundPageShape != null)704+ var elt = origCreateFontElement.apply(this, arguments);705+706+ try399707 {400- var bds = view.getBackgroundPageBounds();401- 402- x0 = 1 + bds.x;403- y0 = 1 + bds.y;708+ if (elt != null && elt.nodeName.toLowerCase() == 'link' &&709+ elt.getAttribute('type') == 'text/css' &&710+ elt.getAttribute('rel') == 'stylesheet')711+ {712+ processGoogleFontCss(elt.getAttribute('href'));713+ }404714 }405- 406- // Computes the offset to maintain origin for grid407- var position = -Math.round(phase - mxUtils.mod(view.translate.x * view.scale - x0, phase)) + 'px ' +408- -Math.round(phase - mxUtils.mod(view.translate.y * view.scale - y0, phase)) + 'px';409- 410- var pages = document.querySelectorAll('[id^=mxPage]');411- 412- var cssTxt = 'margin: 0;padding: 0;background-image: ' + gridImage + ';background-position: ' + position;413- document.body.style.cssText = cssTxt;414-415- for (var i = 0; i < pages.length; i++)715+ catch (e)416716 {417- pages[i].style.cssText = cssTxt;717+ // ignore stylesheet418718 }719+720+ return elt;419721 };420- 722+723+ // Adds wait counter for loading fonts421724 var origAddFont = Graph.addFont;422725 423726 Graph.addFont = function(name, url)
@@ -425,20 +728,28 @@ function render(data)
425728 waitCounter++;426729 return origAddFont.call(this, name, url, decrementWaitCounter); 427730 };428- 429- function renderPage()731+ 732+ /**733+ * Renders the given page or all pages.734+ */735+ function renderPage(currentPageId)430736 {431- // Enables math typesetting432- math |= xmlDoc.documentElement.getAttribute('math') == '1';433- 434- //Load external fonts435- var extFonts = xmlDoc.documentElement.getAttribute('extFonts');436- 437- if (extFonts)737+ // Configures math typesetting738+ math = xmlDoc.documentElement.getAttribute('math') == '1';739+ graph.mathEnabled = math;740+741+ // Sets grid size742+ var gs = xmlDoc.documentElement.getAttribute('gridSize');743+744+ if (gs != null)438745 {439- loadExtFonts(extFonts);746+ graph.gridSize = parseInt(gs);440747 }441- 748+ else749+ {750+ graph.gridSize = mxGraph.prototype.gridSize;751+ }752+442753 // Configure graph443754 graph.foldingEnabled = false;444755 graph.setEnabled(false);
@@ -452,6 +763,10 @@ function render(data)
452763 graph.setBackgroundImage(new mxImage(bgImg.src, bgImg.width,453764 bgImg.height, bgImg.x, bgImg.y));454765 }766+ else767+ {768+ graph.setBackgroundImage(null);769+ }455770 456771 // Parses XML into graph457772 var codec = new mxCodec(xmlDoc);
@@ -462,9 +777,9 @@ function render(data)
462777 463778 if (data.format == 'pdf')464779 {465- if (data.bg == 'none')780+ if (data.bg == 'none' || bg == '')466781 {467- bg = null; 782+ bg = null;468783 }469784 else470785 {
@@ -479,8 +794,7 @@ function render(data)
479794 else480795 {481796 // Loads background color482- bg = (data.bg != null && data.bg.length > 0) ?483- data.bg : xmlDoc.documentElement.getAttribute('background');797+ bg = xmlDoc.documentElement.getAttribute('background');484798 485799 // Normalizes values for transparent backgrounds486800 if (bg == 'none' || bg == '')
@@ -492,18 +806,18 @@ function render(data)
492806 if (bg == null && data.format != 'gif' && data.format != 'png' && data.format != 'svg')493807 {494808 bg = '#ffffff';495- } 496- }497- 498- // Sets background color on page499- if (bg != null)500- {501- document.body.style.backgroundColor = bg;809+ }810+ 811+ // Sets background color on page812+ if (bg != null)813+ {814+ document.body.style.backgroundColor = bg;815+ }502816 }503817 504818 //handle layers505819 if (extras != null && ((extras.layers != null && extras.layers.length > 0) || 506- (extras.layerIds != null && extras.layerIds.length > 0)))820+ (extras.layerIds != null && extras.layerIds.length > 0)))507821 {508822 var childCount = model.getChildCount(model.root);509823
@@ -535,45 +849,14 @@ function render(data)
535849 }536850 537851 // Sets initial value for PDF page background852+ var gb = graph.getGraphBounds();538853 graph.pdfPageVisible = false;539854 540855 // Handles PDF output where the output should match the page format if the page is visible541- if (data.print || (data.format == 'pdf' && xmlDoc.documentElement.getAttribute('page') == '1' && data.w == 0 && data.h == 0 && data.scale == 1))856+ if (data.print || data.format == 'pdf')542857 {543- //Electron printing544858 var printScale = 1;545859 546- if (data.print)547- {548- document.title = data.fileTitle;549- 550- var gb = graph.getGraphBounds();551- printScale = data.pageScale;552- 553- if (isNaN(printScale))554- {555- printScale = 1;556- }557- 558- if (data.fit)559- {560- var h = parseInt(data.sheetsAcross);561- var v = parseInt(data.sheetsDown);562- 563- data.scale = Math.min((data.pageHeight * v) / (gb.height / graph.view.scale),564- (data.pageWidth * h) / (gb.width / graph.view.scale));565- }566- else567- {568- data.scale = data.scale / graph.pageScale;569- 570- if (isNaN(data.scale))571- {572- printScale = 1 / graph.pageScale;573- }574- }575- }576- 577860 var pw = data.pageWidth || xmlDoc.documentElement.getAttribute('pageWidth');578861 var ph = data.pageHeight || xmlDoc.documentElement.getAttribute('pageHeight');579862 graph.pdfPageVisible = true;
@@ -589,7 +872,35 @@ function render(data)
589872 {590873 graph.pageScale = ps;591874 }592- 875+876+ var pf = graph.pageFormat;877+ var temp = data.scale;878+ pf.width = Math.ceil(pf.width * graph.pageScale);879+ pf.height = Math.ceil(pf.height * graph.pageScale);880+ var scale = 1;881+882+ if (data.fit == '1' && data.sheetsAcross != null && data.sheetsDown != null)883+ {884+ var h = data.sheetsAcross;885+ var v = data.sheetsDown;886+887+ if (!isNaN(temp))888+ {889+ pf.width = Math.ceil(pf.width * temp);890+ pf.height = Math.ceil(pf.height * temp);891+ }892+ 893+ scale = Math.min((pf.height * v) / (gb.height / graph.view.scale),894+ (pf.width * h) / (gb.width / graph.view.scale));895+ }896+ else897+ {898+ scale = !isNaN(temp) ? temp : 1;899+ }900+901+ // Applies print scale902+ data.scale = scale * printScale;903+593904 graph.getPageSize = function()594905 {595906 return new mxRectangle(0, 0, this.pageFormat.width * this.pageScale,
@@ -628,7 +939,8 @@ function render(data)
628939 var layout = this.graph.getPageLayout();629940 var page = this.graph.getPageSize();630941 631- return new mxRectangle(this.scale * (this.translate.x + layout.x * page.width),942+ return new mxRectangle(943+ this.scale * (this.translate.x + layout.x * page.width),632944 this.scale * (this.translate.y + layout.y * page.height),633945 this.scale * layout.width * page.width,634946 this.scale * layout.height * page.height);
@@ -721,27 +1033,35 @@ function render(data)
7211033 bounds = (graph.pdfPageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds();7221034 bounds.width = Math.ceil(bounds.width + data.border) + 1; //The 1 extra pixels to prevent cutting the cells on the edges when crop is enabled7231035 bounds.height = Math.ceil(bounds.height + data.border) + 1; //The 1 extra pixels to prevent starting a new page. TODO Not working in every case1036+ 1037+ // Print to pdf fails for 1x1 pages1038+ if (bounds.width <= 1 && bounds.height <= 1)1039+ {1040+ bounds.width = 2;1041+ bounds.height = 2;1042+ }1043+7241044 expScale = graph.view.scale || 1;7251045 7261046 // Converts the graph to a vertical sequence of pages for PDF export7271047 if (graph.pdfPageVisible)7281048 {7291049 var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;730- var scale = data.print? data.scale : 1 / graph.pageScale;731- var autoOrigin = (data.print && data.fit != null) ? data.fit : false;1050+ var scale = (data.print || data.format == 'pdf') ? data.scale : 1 / graph.pageScale;1051+ var autoOrigin = ((data.print || data.format == 'pdf') && data.fit == '1') ||1052+ data.crop == '1' || xmlDoc.documentElement.getAttribute('page') != '1';7321053 var border = 0;7331054 7341055 // Negative coordinates are cropped or shifted if page visible735- var gb = graph.getGraphBounds();7361056 var x0 = 0;7371057 var y0 = 0;7381058 739- // Applies print scale740- pf = mxRectangle.fromRectangle(pf);741- pf.width = Math.ceil(pf.width * printScale) + 1; //The 1 extra pixels to prevent cutting the cells on the right edge of the page742- pf.height = Math.ceil(pf.height * printScale) + 1; //The 1 extra pixels to prevent starting a new page. TODO Not working in every case743- scale *= printScale; 744- 1059+ if (data.crop == '1')1060+ {1061+ pf.width = (gb.width + 1.5) * scale;1062+ pf.height = (gb.height + 1.5) * scale;1063+ }1064+7451065 // Starts at first visible page7461066 if (!autoOrigin)7471067 {
@@ -749,6 +1069,8 @@ function render(data)
7491069 x0 -= layout.x * pf.width;7501070 y0 -= layout.y * pf.height;7511071 }1072+1073+ var anchorId = (currentPageId != null) ? 'page/id,' + currentPageId : null;7521074 7531075 if (preview == null)7541076 {
@@ -756,36 +1078,71 @@ function render(data)
7561078 preview.printBackgroundImage = true;7571079 preview.autoOrigin = autoOrigin;7581080 preview.backgroundColor = bg;1081+ preview.pageMargin = (data.pageMargin != null) ?1082+ parseInt(data.pageMargin) : ((data.crop == '1') ?1083+ 0 : preview.pageMargin);1084+ 1085+ // Replaces background images with SVG subtrees1086+ var previewDrawBackgroundImage = preview.drawBackgroundImage;1087+ 1088+ preview.drawBackgroundImage = function(img)1089+ {1090+ previewDrawBackgroundImage.apply(this, arguments);1091+1092+ if (img.node != null)1093+ {1094+ EditorUi.embedSvgImages(img.node);1095+1096+ graph.disableSvgLinks(img.node, function(link)1097+ {1098+ link.setAttribute('href', 'javascript:void(0)'); 1099+ });1100+ }1101+ };1102+1103+ // Renders the grid and configures math1104+ var previewAddGraphFragment = preview.addGraphFragment;1105+1106+ preview.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip)1107+ {1108+ previewAddGraphFragment.apply(this, arguments);1109+1110+ // Disables math rendering in graph1111+ if (!graph.mathEnabled)1112+ {1113+ div.classList.add('geDisableMathJax')1114+ }1115+ 1116+ // Adds shadow1117+ if (xmlDoc.documentElement.getAttribute('shadow') == '1')1118+ {1119+ var svgs = div.getElementsByTagName('svg');1120+ 1121+ for (var i = 0; i < svgs.length; i++)1122+ {1123+ graph.addSvgShadow(svgs[i]);1124+ }1125+ }1126+ 1127+ waitForGoogleFontImports(div);1128+ };1129+7591130 // Renders print output into this document and removes the graph container760- preview.open(null, window);761- graph.container.parentNode.removeChild(graph.container);1131+ preview.gridColor = gridColor;1132+ preview.gridSize = graph.gridSize;1133+ preview.gridSteps = graph.view.gridSteps;1134+ preview.open(null, window, null, null, anchorId, pf);7621135 }7631136 else7641137 {1138+ preview.gridColor = gridColor;1139+ preview.gridSize = graph.gridSize;1140+ preview.gridSteps = graph.view.gridSteps;7651141 preview.backgroundColor = bg;7661142 preview.autoOrigin = autoOrigin; 767- preview.appendGraph(graph, scale, x0, y0);1143+ preview.appendGraph(graph, scale, x0, y0, null, null, anchorId, pf);7681144 }7691145 770- // Adds shadow771- // NOTE: Shadow rasterizes output772- /*if (mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')773- {774- var svgs = document.getElementsByTagName('svg');775- 776- for (var i = 0; i < svgs.length; i++)777- {778- var svg = svgs[i];779-780- var filter = graph.addSvgShadow(svg, null, true);781- filter.setAttribute('id', 'shadow-' + i);782- svg.appendChild(filter);783- svg.setAttribute('filter', 'url(#' + 'shadow-' + i + ')');784- }785- 786- border = 7;787- }*/788- 7891146 bounds = new mxRectangle(0, 0, pf.width, pf.height);7901147 }7911148 else
@@ -800,16 +1157,23 @@ function render(data)
8001157 bounds.add(new mxRectangle(8011158 (t.x + bgImg.x) * s, (t.y + bgImg.y) * s,8021159 bgImg.width * s, bgImg.height * s));1160+1161+ if (t.x < 0 || t.y < 0)1162+ {1163+ graph.view.setTranslate(1164+ t.x < 0 ? Math.max(-bgImg.x * s, t.x) : t.x,1165+ t.y < 0 ? Math.max(-bgImg.y * s, t.y) : t.y);1166+ bounds.x = 0.5;1167+ bounds.y = 0.5;1168+ }8031169 }8041170 8051171 // Adds shadow806- // NOTE: PDF shadow rasterizes output so it's disabled807- if (data.format != 'pdf' && mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')1172+ if (xmlDoc.documentElement.getAttribute('shadow') == '1')8081173 {809- graph.addSvgShadow(graph.view.canvas.ownerSVGElement, null, true);810- graph.setShadowVisible(true);811- bounds.width += 7;812- bounds.height += 7;1174+ var size = graph.setShadowVisible(true);1175+ bounds.width += size;1176+ bounds.height += size;8131177 }8141178 8151179 document.body.style.width = Math.ceil(bounds.x + bounds.width) + 'px';
@@ -821,8 +1185,8 @@ function render(data)
8211185 {8221186 var to = diagrams.length - 1;8231187 824- //Parameters to and all pages should not be sent with formats other than PDF with page view enabled825- if (!data.allPages)1188+ // Parameters to and all pages should not be sent with formats other than PDF with page view enabled1189+ if (data.allPages != '1')8261190 {8271191 if (data.pageId != null)8281192 {
@@ -857,21 +1221,23 @@ function render(data)
8571221 return (diagrams == null) ? 'Page-1' :8581222 (diagrams[from].getAttribute('name') || ('Page-' + (from + 1)));8591223 }860- else if (name == 'pagenumber')1224+ else if (name == 'pagenumber' && data.from != null)8611225 {8621226 return from + 1;8631227 }8641228 8651229 return graphGetGlobalVariable.apply(this, arguments);8661230 };867- 1231+1232+ waitCounter += to - from + 1;1233+ 8681234 for (var i = from; i <= to; i++) 8691235 {8701236 if (diagrams[i] != null)8711237 {8721238 if (pageId == null)8731239 {874- pageId = diagrams[i].getAttribute('id')1240+ pageId = diagrams[i].getAttribute('id');8751241 }8761242 8771243 xmlDoc = Editor.parseDiagramNode(diagrams[i]);
@@ -883,29 +1249,42 @@ function render(data)
8831249 8841250 graph.getModel().clear();8851251 from = i;886- renderPage();1252+ renderPage(diagrams[i].getAttribute('id'));8871253 }1254+1255+ decrementWaitCounter();8881256 }8891257 }8901258 else8911259 {8921260 renderPage();8931261 }894- 1262+1263+ if (preview != null)1264+ {1265+ preview.addPendingCss(document);1266+ Graph.rewritePageLinks(document, true);1267+ }1268+8951269 if (fallbackFont)8961270 {897- //Add a fallbackFont font to all labels in case the selected font doesn't support the character898- //Some systems doesn't have a good fallback fomt that supports all languages899- //Use this with a custom font-face in export-fonts.css file1271+ // Add a fallbackFont font to all labels in case the selected font doesn't support the character1272+ // Some systems doesn't have a good fallback fomt that supports all languages1273+ // Use this with a custom font-face in export-fonts.css file9001274 document.querySelectorAll('foreignObject div').forEach(d => d.style.fontFamily = (d.style.fontFamily || '') + ', ' + fallbackFont);9011275 }902- 903- renderGrid();1276+1277+ if (data.format == 'pdf')1278+ {1279+ graph.container.parentNode.removeChild(graph.container);1280+ }1281+9041282 // Includes images in SVG and HTML labels9051283 waitForImages('image', 'xlink:href');9061284 waitForImages('img', 'src');9071285 renderMath(document.body);908- // Immediate return if not waiting for any content1286+ 1287+ // Invokes callback9091288 decrementWaitCounter();9101289 9111290 return graph;
@@ -920,7 +1299,14 @@ if (mxIsElectron)
9201299 {9211300 try9221301 {923- render(arg);1302+ if (window.shapesLoaded)1303+ {1304+ render(arg);1305+ }1306+ else1307+ {1308+ window.pendingRequest = arg;1309+ }9241310 }9251311 catch(e)9261312 {
M diagrams.net/import.sh +2 -0
A diagramzip-client-units/diagramsnet/.gitignore +3 -0
A diagramzip-client-units/diagramsnet/package-lock.json +499 -0
@@ -0,0 +1,499 @@
1+{2+ "name": "@diagramzip/renderer-diagramsnet",3+ "version": "1.0.0",4+ "lockfileVersion": 3,5+ "requires": true,6+ "packages": {7+ "": {8+ "name": "@diagramzip/renderer-diagramsnet",9+ "version": "1.0.0",10+ "devDependencies": {11+ "esbuild": "0.28.1"12+ }13+ },14+ "node_modules/@esbuild/aix-ppc64": {15+ "version": "0.28.1",16+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",17+ "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",18+ "cpu": [19+ "ppc64"20+ ],21+ "dev": true,22+ "license": "MIT",23+ "optional": true,24+ "os": [25+ "aix"26+ ],27+ "engines": {28+ "node": ">=18"29+ }30+ },31+ "node_modules/@esbuild/android-arm": {32+ "version": "0.28.1",33+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",34+ "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",35+ "cpu": [36+ "arm"37+ ],38+ "dev": true,39+ "license": "MIT",40+ "optional": true,41+ "os": [42+ "android"43+ ],44+ "engines": {45+ "node": ">=18"46+ }47+ },48+ "node_modules/@esbuild/android-arm64": {49+ "version": "0.28.1",50+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",51+ "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",52+ "cpu": [53+ "arm64"54+ ],55+ "dev": true,56+ "license": "MIT",57+ "optional": true,58+ "os": [59+ "android"60+ ],61+ "engines": {62+ "node": ">=18"63+ }64+ },65+ "node_modules/@esbuild/android-x64": {66+ "version": "0.28.1",67+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",68+ "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",69+ "cpu": [70+ "x64"71+ ],72+ "dev": true,73+ "license": "MIT",74+ "optional": true,75+ "os": [76+ "android"77+ ],78+ "engines": {79+ "node": ">=18"80+ }81+ },82+ "node_modules/@esbuild/darwin-arm64": {83+ "version": "0.28.1",84+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",85+ "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",86+ "cpu": [87+ "arm64"88+ ],89+ "dev": true,90+ "license": "MIT",91+ "optional": true,92+ "os": [93+ "darwin"94+ ],95+ "engines": {96+ "node": ">=18"97+ }98+ },99+ "node_modules/@esbuild/darwin-x64": {100+ "version": "0.28.1",101+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",102+ "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",103+ "cpu": [104+ "x64"105+ ],106+ "dev": true,107+ "license": "MIT",108+ "optional": true,109+ "os": [110+ "darwin"111+ ],112+ "engines": {113+ "node": ">=18"114+ }115+ },116+ "node_modules/@esbuild/freebsd-arm64": {117+ "version": "0.28.1",118+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",119+ "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",120+ "cpu": [121+ "arm64"122+ ],123+ "dev": true,124+ "license": "MIT",125+ "optional": true,126+ "os": [127+ "freebsd"128+ ],129+ "engines": {130+ "node": ">=18"131+ }132+ },133+ "node_modules/@esbuild/freebsd-x64": {134+ "version": "0.28.1",135+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",136+ "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",137+ "cpu": [138+ "x64"139+ ],140+ "dev": true,141+ "license": "MIT",142+ "optional": true,143+ "os": [144+ "freebsd"145+ ],146+ "engines": {147+ "node": ">=18"148+ }149+ },150+ "node_modules/@esbuild/linux-arm": {151+ "version": "0.28.1",152+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",153+ "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",154+ "cpu": [155+ "arm"156+ ],157+ "dev": true,158+ "license": "MIT",159+ "optional": true,160+ "os": [161+ "linux"162+ ],163+ "engines": {164+ "node": ">=18"165+ }166+ },167+ "node_modules/@esbuild/linux-arm64": {168+ "version": "0.28.1",169+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",170+ "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",171+ "cpu": [172+ "arm64"173+ ],174+ "dev": true,175+ "license": "MIT",176+ "optional": true,177+ "os": [178+ "linux"179+ ],180+ "engines": {181+ "node": ">=18"182+ }183+ },184+ "node_modules/@esbuild/linux-ia32": {185+ "version": "0.28.1",186+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",187+ "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",188+ "cpu": [189+ "ia32"190+ ],191+ "dev": true,192+ "license": "MIT",193+ "optional": true,194+ "os": [195+ "linux"196+ ],197+ "engines": {198+ "node": ">=18"199+ }200+ },201+ "node_modules/@esbuild/linux-loong64": {202+ "version": "0.28.1",203+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",204+ "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",205+ "cpu": [206+ "loong64"207+ ],208+ "dev": true,209+ "license": "MIT",210+ "optional": true,211+ "os": [212+ "linux"213+ ],214+ "engines": {215+ "node": ">=18"216+ }217+ },218+ "node_modules/@esbuild/linux-mips64el": {219+ "version": "0.28.1",220+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",221+ "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",222+ "cpu": [223+ "mips64el"224+ ],225+ "dev": true,226+ "license": "MIT",227+ "optional": true,228+ "os": [229+ "linux"230+ ],231+ "engines": {232+ "node": ">=18"233+ }234+ },235+ "node_modules/@esbuild/linux-ppc64": {236+ "version": "0.28.1",237+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",238+ "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",239+ "cpu": [240+ "ppc64"241+ ],242+ "dev": true,243+ "license": "MIT",244+ "optional": true,245+ "os": [246+ "linux"247+ ],248+ "engines": {249+ "node": ">=18"250+ }251+ },252+ "node_modules/@esbuild/linux-riscv64": {253+ "version": "0.28.1",254+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",255+ "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",256+ "cpu": [257+ "riscv64"258+ ],259+ "dev": true,260+ "license": "MIT",261+ "optional": true,262+ "os": [263+ "linux"264+ ],265+ "engines": {266+ "node": ">=18"267+ }268+ },269+ "node_modules/@esbuild/linux-s390x": {270+ "version": "0.28.1",271+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",272+ "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",273+ "cpu": [274+ "s390x"275+ ],276+ "dev": true,277+ "license": "MIT",278+ "optional": true,279+ "os": [280+ "linux"281+ ],282+ "engines": {283+ "node": ">=18"284+ }285+ },286+ "node_modules/@esbuild/linux-x64": {287+ "version": "0.28.1",288+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",289+ "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",290+ "cpu": [291+ "x64"292+ ],293+ "dev": true,294+ "license": "MIT",295+ "optional": true,296+ "os": [297+ "linux"298+ ],299+ "engines": {300+ "node": ">=18"301+ }302+ },303+ "node_modules/@esbuild/netbsd-arm64": {304+ "version": "0.28.1",305+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",306+ "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",307+ "cpu": [308+ "arm64"309+ ],310+ "dev": true,311+ "license": "MIT",312+ "optional": true,313+ "os": [314+ "netbsd"315+ ],316+ "engines": {317+ "node": ">=18"318+ }319+ },320+ "node_modules/@esbuild/netbsd-x64": {321+ "version": "0.28.1",322+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",323+ "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",324+ "cpu": [325+ "x64"326+ ],327+ "dev": true,328+ "license": "MIT",329+ "optional": true,330+ "os": [331+ "netbsd"332+ ],333+ "engines": {334+ "node": ">=18"335+ }336+ },337+ "node_modules/@esbuild/openbsd-arm64": {338+ "version": "0.28.1",339+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",340+ "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",341+ "cpu": [342+ "arm64"343+ ],344+ "dev": true,345+ "license": "MIT",346+ "optional": true,347+ "os": [348+ "openbsd"349+ ],350+ "engines": {351+ "node": ">=18"352+ }353+ },354+ "node_modules/@esbuild/openbsd-x64": {355+ "version": "0.28.1",356+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",357+ "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",358+ "cpu": [359+ "x64"360+ ],361+ "dev": true,362+ "license": "MIT",363+ "optional": true,364+ "os": [365+ "openbsd"366+ ],367+ "engines": {368+ "node": ">=18"369+ }370+ },371+ "node_modules/@esbuild/openharmony-arm64": {372+ "version": "0.28.1",373+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",374+ "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",375+ "cpu": [376+ "arm64"377+ ],378+ "dev": true,379+ "license": "MIT",380+ "optional": true,381+ "os": [382+ "openharmony"383+ ],384+ "engines": {385+ "node": ">=18"386+ }387+ },388+ "node_modules/@esbuild/sunos-x64": {389+ "version": "0.28.1",390+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",391+ "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",392+ "cpu": [393+ "x64"394+ ],395+ "dev": true,396+ "license": "MIT",397+ "optional": true,398+ "os": [399+ "sunos"400+ ],401+ "engines": {402+ "node": ">=18"403+ }404+ },405+ "node_modules/@esbuild/win32-arm64": {406+ "version": "0.28.1",407+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",408+ "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",409+ "cpu": [410+ "arm64"411+ ],412+ "dev": true,413+ "license": "MIT",414+ "optional": true,415+ "os": [416+ "win32"417+ ],418+ "engines": {419+ "node": ">=18"420+ }421+ },422+ "node_modules/@esbuild/win32-ia32": {423+ "version": "0.28.1",424+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",425+ "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",426+ "cpu": [427+ "ia32"428+ ],429+ "dev": true,430+ "license": "MIT",431+ "optional": true,432+ "os": [433+ "win32"434+ ],435+ "engines": {436+ "node": ">=18"437+ }438+ },439+ "node_modules/@esbuild/win32-x64": {440+ "version": "0.28.1",441+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",442+ "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",443+ "cpu": [444+ "x64"445+ ],446+ "dev": true,447+ "license": "MIT",448+ "optional": true,449+ "os": [450+ "win32"451+ ],452+ "engines": {453+ "node": ">=18"454+ }455+ },456+ "node_modules/esbuild": {457+ "version": "0.28.1",458+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",459+ "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",460+ "dev": true,461+ "hasInstallScript": true,462+ "license": "MIT",463+ "bin": {464+ "esbuild": "bin/esbuild"465+ },466+ "engines": {467+ "node": ">=18"468+ },469+ "optionalDependencies": {470+ "@esbuild/aix-ppc64": "0.28.1",471+ "@esbuild/android-arm": "0.28.1",472+ "@esbuild/android-arm64": "0.28.1",473+ "@esbuild/android-x64": "0.28.1",474+ "@esbuild/darwin-arm64": "0.28.1",475+ "@esbuild/darwin-x64": "0.28.1",476+ "@esbuild/freebsd-arm64": "0.28.1",477+ "@esbuild/freebsd-x64": "0.28.1",478+ "@esbuild/linux-arm": "0.28.1",479+ "@esbuild/linux-arm64": "0.28.1",480+ "@esbuild/linux-ia32": "0.28.1",481+ "@esbuild/linux-loong64": "0.28.1",482+ "@esbuild/linux-mips64el": "0.28.1",483+ "@esbuild/linux-ppc64": "0.28.1",484+ "@esbuild/linux-riscv64": "0.28.1",485+ "@esbuild/linux-s390x": "0.28.1",486+ "@esbuild/linux-x64": "0.28.1",487+ "@esbuild/netbsd-arm64": "0.28.1",488+ "@esbuild/netbsd-x64": "0.28.1",489+ "@esbuild/openbsd-arm64": "0.28.1",490+ "@esbuild/openbsd-x64": "0.28.1",491+ "@esbuild/openharmony-arm64": "0.28.1",492+ "@esbuild/sunos-x64": "0.28.1",493+ "@esbuild/win32-arm64": "0.28.1",494+ "@esbuild/win32-ia32": "0.28.1",495+ "@esbuild/win32-x64": "0.28.1"496+ }497+ }498+ }499+}
A diagramzip-client-units/diagramsnet/package.json +12 -0
A diagramzip-client-units/diagramsnet/scripts/build.mjs +19 -0
A diagramzip-client-units/diagramsnet/scripts/check.mjs +16 -0
A diagramzip-client-units/diagramsnet/src/frame.js +50 -0
@@ -0,0 +1,50 @@
1+const CHANNEL = 'diagram.zip:renderer:v1'2+const ENGINE = 'diagramsnet'3+const VERSION = 'diagrams.net@29.6.1'4+const BUILD = 'diagramsnet-29.6.1-client-unit-1'5+const MAX_SOURCE_LENGTH = 524_2886+const MAX_OUTPUT_LENGTH = 4_194_3047+const RENDER_TIMEOUT = 20_0008+9+let latest = 010+let queue = Promise.resolve()11+12+function reply(origin, message) {13+ parent.postMessage({ channel: CHANNEL, ...message }, origin)14+}15+16+function allowedOrigin() {17+ try { return document.referrer ? new URL(document.referrer).origin : null } catch { return null }18+}19+20+async function renderMessage(message, origin, sequence) {21+ if (sequence !== latest) return22+ if (typeof message.source !== 'string' || message.source.length > MAX_SOURCE_LENGTH) throw new Error('Diagrams.net source must be a string no larger than 512 KB.')23+ document.getElementById('LoadingComplete')?.remove()24+ const graph = render({ xml: message.source, format: 'svg' })25+ const deadline = Date.now() + RENDER_TIMEOUT26+ while (!document.getElementById('LoadingComplete')) {27+ if (sequence !== latest) return28+ if (Date.now() >= deadline) throw new Error('Diagrams.net rendering timed out.')29+ await new Promise(resolve => setTimeout(resolve, 10))30+ }31+ if (sequence !== latest) return32+ const background = graph.background === mxConstants.NONE ? null : graph.background33+ const svg = new XMLSerializer().serializeToString(graph.getSvg(background, 1, 0, false, null, true, null, null))34+ if (svg.length > MAX_OUTPUT_LENGTH) throw new Error('Diagrams.net SVG output is too large.')35+ reply(origin, { type: 'result', requestId: message.requestId, ok: true, svg, version: VERSION, build: BUILD, pipeline: [ENGINE] })36+}37+38+window.addEventListener('message', event => {39+ const message = event.data40+ const origin = allowedOrigin()41+ if (event.source !== parent || message?.channel !== CHANNEL || message?.type !== 'render' || message?.engine !== ENGINE || typeof message?.requestId !== 'string') return42+ const sequence = ++latest43+ queue = queue.then(async () => {44+ if (sequence !== latest) return45+ try { await renderMessage(message, origin ?? '*', sequence) }46+ catch (error) { if (sequence === latest) reply(origin ?? '*', { type: 'result', requestId: message.requestId, ok: false, error: error instanceof Error ? error.message.slice(0, 500) : 'Diagrams.net rendering failed.' }) }47+ })48+})49+50+reply(allowedOrigin() ?? '*', { type: 'ready', engine: ENGINE, version: VERSION, build: BUILD })
A diagramzip-client-units/diagramsnet/unit/_headers +4 -0
A diagramzip-client-units/diagramsnet/unit/index.html +14 -0
A diagramzip-client-units/diagramsnet/unit/worker.js +32 -0
A diagramzip-client-units/diagramsnet/wrangler.jsonc +15 -0
M diagramzip/src/client-renderers.js +1 -0
M diagramzip/test/client-renderers.test.js +2 -1
M docs/refactor-engines.md +1 -1
@@ -138,7 +138,7 @@ These engines should primarily stop being live-preview server responsibilities.
138138 | C4-PlantUML | PlantUML browser path plus pinned C4 macros/includes | Origin initially | Pin and test the precise macro set rather than permitting arbitrary remote includes |139139 | BPMN | `bpmn-js`: import XML and export SVG | Frozen SVG or browser-render service | No backend renderer is required for normal preview/export |140140 | Excalidraw | Official `exportToSvg()` | Frozen SVG or browser-render service | Lazy-load fonts and assets; preserve Excalidraw semantics |141-| diagrams.net | Official client export application/iframe | Existing origin fallback initially | Large asset surface and complex integration; isolate it from the core application bundle |141+| diagrams.net | Pinned diagrams.net 29.6.1 export application in a sandboxed iframe | Existing origin fallback initially | The official export application is browser/DOM-bound; keep its immutable asset tree isolated from the core application bundle |142142 | UMLet | UMLetino/GWT web implementation | Origin initially | Requires an adapter and parity spike before removing the fallback |143143 144144 Untrusted source must be rendered in a sandboxed iframe, not directly in the application document. The frame should receive source by `postMessage`, render with self-hosted immutable assets, block external requests in secure mode, serialize the result, and return it to the parent. The parent should sanitize the SVG and display it through a Blob-backed image rather than inserting raw renderer markup into the application DOM.
M server/src/main/resources/web/diagramzip/assets/index-CzBQjn0p.js +1 -1
@@ -1627,7 +1627,7 @@ Rel(bob, tandem, "Completes tasks")
16271627 +--------+--------+16281628 | Shared Tasks |16291629 | {s} |1630- +-----------------+`,d2:c,nwdiag:h,packetdiag:ee,rackdiag:g,bpmn:o.replaceAll(`Examine Situation`,`Bob Agent tests Tandem`).replaceAll(`Things OK?`,`Tests pass?`).replaceAll(`Notification Sent`,`Alice and Bob share tasks`).replaceAll(`Error Propagated`,`Alice Agent revises flow`).replaceAll(`Klaus`,`Alice`).replaceAll(`Walter`,`Bob`),bytefield:s,dbml:l,diagramsnet:u,erd:d,excalidraw:f,goat:p,nomnoml:m,pikchr:te,svgbob:_,symbolator:ne,tikz:re,umlet:ie,vega:ae,vegalite:v,wavedrom:oe,wireviz:se},st={plantuml:[`Tandem task sync`,`Alice and Bob coordinate shared tasks through their agents.`],mermaid:[`Shared task flow`,`A compact view of ideas moving through the Tandem team.`],graphviz:[`Tandem collaboration graph`,`People and agents converge on one synchronized task list.`],d2:[`Connection styles`,`D2 connection, arrowhead, shape, and animation syntax.`],c4plantuml:[`Tandem system context`,`The people and software systems surrounding Tandem.`],blockdiag:[`Tandem block flow`,`A block-oriented view of the shared task workflow.`],seqdiag:[`Tandem task sequence`,`A task moves from Alice to Bob and back through Tandem.`],actdiag:[`Tandem delivery activities`,`Work crosses Alice, Alice Agent, and Bob Agent lanes.`],nwdiag:[`Network layout`,`A sample network topology and its connected address ranges.`],packetdiag:[`TCP packet structure`,`The bit-level layout of a TCP packet header.`],rackdiag:[`Rack layout`,`A sample equipment rack rendered from rackdiag syntax.`],bpmn:[`Agent-assisted review`,`A BPMN review flow that branches on test results.`],bytefield:[`Byte field layout`,`A compact byte-oriented protocol field example.`],dbml:[`Database schema`,`Tables and relationships expressed with DBML.`],diagramsnet:[`Venn diagram`,`An overlapping-set example imported from diagrams.net.`],ditaa:[`Tandem ASCII architecture`,`An ASCII sketch becomes a rendered system diagram.`],erd:[`Data model`,`Entities and relationships in a small relational schema.`],excalidraw:[`Venn sketch`,`A hand-drawn overlapping-set diagram.`],goat:[`Component shapes`,`GoAT exercises boxes, connectors, curves, and symbols.`],nomnoml:[`Pirate class diagram`,`A playful class diagram demonstrating Nomnoml syntax.`],pikchr:[`Cardinal points`,`A diamond marks north, east, south, and west.`],structurizr:[`Tandem landscape`,`A software landscape linking people, agents, and Tandem.`],svgbob:[`Cloud network`,`An ASCII cloud connects clients and services.`],symbolator:[`Hardware component`,`A component symbol generated from interface declarations.`],tikz:[`Periodic table`,`A color-coded periodic table rendered with TikZ.`],umlet:[`UML example`,`A UMLet document rendered directly from its XML form.`],vega:[`Bar chart`,`A declarative bar chart expressed with Vega.`],vegalite:[`Discretizing scale`,`A Vega-Lite example comparing discretized values.`],wavedrom:[`Timing diagram`,`Digital signals and timing relationships rendered by WaveDrom.`],wireviz:[`Wiring harness`,`Connections, cables, and pins described with WireViz.`]};function ct(e){return ot[e]??at}function lt(e){let t=n.findIndex(t=>t.id===e),[r,i]=st[e]??[`Diagram example`,`A diagram.zip catalog example.`];return{type:e,source:ct(e),options:{},...le(Math.max(t,0),{title:r,description:i})}}function ut({search:e=globalThis.location?.search??``,coarsePointer:t=globalThis.matchMedia?.(`(pointer: coarse)`).matches??!1}={}){let n=new URLSearchParams(e).get(`editor`);return n===`textarea`||n===`monaco`?n:t?`textarea`:`monaco`}async function dt(e){let t=ut();try{return await ft(t,e)}catch(n){if(t===`textarea`)throw n;return console.warn(`Monaco Editor is unavailable; using the basic text editor.`,n),ft(`textarea`,e)}}async function ft(e,n){let r=await(e===`textarea`?await t(()=>import(`./textarea-editor-BTXrLHn4.js`),[]):await t(()=>import(`./monaco-editor-C7iaFm9N.js`),__vite__mapDeps([0,1,2,3,4,5]))).createEditor(n);return n.element.dataset.editorBackend=e,r}function pt(e,t,n,r,i){return e.set(t,{...r,type:t}),e.get(n)??i(n)}var mt=`/api/v1`,U=/^[A-Za-z0-9_-]{16}$/,ht=/^[A-Za-z0-9_-]{43}$/;function gt(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/,``)}function _t(){return gt(crypto.getRandomValues(new Uint8Array(32)))}function vt(e){return e.match(/^\/d\/([A-Za-z0-9_-]{16})\/?$/)?.[1]??null}function yt(e,t){if(!U.test(t))throw Error(`Invalid diagram alias.`);return new URL(`/d/${t}`,e).toString()}function bt(e,t,n){if(!ht.test(n))throw Error(`Invalid write capability.`);let r=new URL(yt(e,t));return r.hash=`w=${n}`,r.toString()}function xt(e,t,n=`svg`){if(!U.test(t))throw Error(`Invalid diagram alias.`);if(n!==`svg`&&n!==`png`)throw Error(`Invalid render format.`);return new URL(`${mt}/aliases/${t}/renders/${n}`,e).toString()}function St(e){return e.match(/^#w=([A-Za-z0-9_-]{43})$/)?.[1]??null}var Ct=class extends Error{constructor(e,{status:t,code:n}={}){super(e),this.status=t,this.code=n}};function wt(e){if(!e||typeof e!=`object`||typeof e.type!=`string`||typeof e.source!=`string`)throw Error(`Invalid diagram state.`);return{mode:`open`,diagram:{type:e.type,source:e.source,options:e.options??{},presentation:H(e.presentation)},metadata:V(e.meta)}}function Tt(e){if(!e||typeof e!=`object`||!U.test(e.aliasId)||!ht.test(e.contentId)||!ht.test(e.renderId)||!Number.isSafeInteger(e.revision)||e.mode!==`open`&&e.mode!==`locked`)throw new Ct(`The persistence service returned an invalid response.`,{code:`invalid_response`});let t={aliasId:e.aliasId,contentId:e.contentId,renderId:e.renderId,revision:e.revision,mode:e.mode,createdAt:e.createdAt,updatedAt:e.updatedAt};if(e.mode===`locked`){if(!e.encryptedContent||!e.encryptedMetadata||!e.keyEnvelope)throw new Ct(`The persistence service returned an invalid locked diagram.`,{code:`invalid_response`});return{...t,encryptedContent:e.encryptedContent,encryptedMetadata:e.encryptedMetadata,keyEnvelope:e.keyEnvelope}}if(!e.diagram||typeof e.diagram!=`object`)throw new Ct(`The persistence service returned an invalid open diagram.`,{code:`invalid_response`});return{...t,state:{type:e.diagram.type,source:e.diagram.source,options:e.diagram.options??{},presentation:H(e.diagram.presentation),meta:V(e.metadata)}}}var Et=class{constructor({fetchImpl:e=globalThis.fetch,origin:t=globalThis.location?.origin??`http://localhost`,apiPrefix:n=mt}={}){this.fetch=(...t)=>e(...t),this.origin=t,this.apiPrefix=n.replace(/\/$/,``)}url(e=``){return new URL(`${this.apiPrefix}/aliases${e}`,this.origin)}async request(e,t){let n=await this.fetch(e,t),r=await n.json().catch(()=>null);if(!n.ok)throw new Ct(r?.error?.message||`Persistence request failed with HTTP ${n.status}.`,{status:n.status,code:r?.error?.code});return Tt(r)}async errorFor(e){let t=await e.json().catch(()=>null);return new Ct(t?.error?.message||`Persistence request failed with HTTP ${e.status}.`,{status:e.status,code:t?.error?.code})}async createPayload(e){let t=_t();return{...await this.request(this.url(),{method:`POST`,headers:{Authorization:`Bearer ${t}`,"Content-Type":`application/json`},body:JSON.stringify(e)}),writeCapability:t}}createAlias(e){return this.createPayload(wt(e))}createLockedAlias(e){if(e?.mode!==`locked`)throw Error(`Invalid locked diagram payload.`);return this.createPayload(e)}getAlias(e){if(!U.test(e))throw Error(`Invalid diagram alias.`);return this.request(this.url(`/${e}`),{headers:{Accept:`application/json`}})}updatePayload(e,t,n,r){if(!U.test(e))throw Error(`Invalid diagram alias.`);if(!Number.isSafeInteger(n)||n<1)throw Error(`Invalid alias revision.`);if(!ht.test(r))throw Error(`Invalid write capability.`);return this.request(this.url(`/${e}`),{method:`PUT`,headers:{Accept:`application/json`,Authorization:`Bearer ${r}`,"Content-Type":`application/json`,"If-Match":`"${n}"`},body:JSON.stringify(t)})}updateAlias(e,t,n,r){return this.updatePayload(e,wt(t),n,r)}updateLockedAlias(e,t,n,r){if(t?.mode!==`locked`)throw Error(`Invalid locked diagram payload.`);return this.updatePayload(e,t,n,r)}async uploadRender({aliasId:e,renderId:t,revision:n,writeCapability:r,format:i,mode:a,render:o,renderer:s}){if(!U.test(e))throw Error(`Invalid diagram alias.`);if(!ht.test(t))throw Error(`Invalid render ID.`);if(!Number.isSafeInteger(n)||n<1)throw Error(`Invalid alias revision.`);if(!ht.test(r))throw Error(`Invalid write capability.`);if(i!==`svg`&&i!==`png`)throw Error(`Invalid render format.`);if(a!==`open`&&a!==`locked`)throw Error(`Invalid diagram mode.`);if(!s||!/^[a-z][a-z0-9-]{0,31}$/.test(s.unit)||typeof s.build!=`string`||!/^[A-Za-z0-9][A-Za-z0-9._@+-]{0,127}$/.test(s.build)||!Array.isArray(s.pipeline)||s.pipeline.length<1||s.pipeline.some(e=>!/^[a-z][a-z0-9-]{0,31}$/.test(e)))throw Error(`Invalid renderer identity.`);let c=a===`locked`?`application/json`:i===`svg`?`image/svg+xml`:`image/png`,l=a===`locked`?JSON.stringify(o):o,u=await this.fetch(this.url(`/${e}/renders/${i}`),{method:`PUT`,headers:{Authorization:`Bearer ${r}`,"Content-Type":c,"If-Match":`"${n}"`,"X-Render-Id":t,"X-Renderer-Unit":s.unit,"X-Renderer-Build":s.build,"X-Renderer-Pipeline":s.pipeline.join(`,`)},body:l});if(!u.ok)throw await this.errorFor(u)}async getEncryptedRender(e,t){if(!U.test(e))throw Error(`Invalid diagram alias.`);if(t!==`svg`&&t!==`png`)throw Error(`Invalid render format.`);let n=this.url(`/${e}/renders/${t}`);n.searchParams.set(`encrypted`,`1`);let r=await this.fetch(n,{headers:{Accept:`application/json`}});if(!r.ok)throw await this.errorFor(r);return r.json()}},Dt=6e5,Ot=new TextEncoder,kt=new TextDecoder,At=Ot.encode(`diagram.zip:content:v1`),jt=Ot.encode(`diagram.zip:metadata:v1`),Mt=Ot.encode(`diagram.zip:bundle-key:v1`);function Nt(e){if(e!==`svg`&&e!==`png`)throw Error(`Invalid render format.`);return Ot.encode(`diagram.zip:render:${e}:v1`)}function Pt(e){let t=``,n=32768;for(let r=0;r<e.length;r+=n)t+=String.fromCharCode(...e.subarray(r,r+n));return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/,``)}function Ft(e){if(typeof e!=`string`||!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`Invalid encrypted diagram.`);let t=e.replaceAll(`-`,`+`).replaceAll(`_`,`/`),n=t+`=`.repeat((4-t.length%4)%4),r=atob(n),i=Uint8Array.from(r,e=>e.charCodeAt(0));if(Pt(i)!==e)throw Error(`Invalid encrypted diagram.`);return i}function It(e){return crypto.getRandomValues(new Uint8Array(e))}async function Lt(e,t){if(!(e instanceof Uint8Array)||e.byteLength!==32)throw Error(`Invalid diagram key.`);return crypto.subtle.importKey(`raw`,e,{name:`AES-GCM`},!1,t)}async function Rt(e,{iterations:t,salt:n}){if(typeof e!=`string`)throw Error(`Password is required.`);let r=await crypto.subtle.importKey(`raw`,Ot.encode(e),`PBKDF2`,!1,[`deriveKey`]);return crypto.subtle.deriveKey({name:`PBKDF2`,hash:`SHA-256`,iterations:t,salt:n},r,{name:`AES-GCM`,length:256},!1,[`encrypt`,`decrypt`])}async function zt(e,t,n){let r=It(12),i=await crypto.subtle.encrypt({name:`AES-GCM`,iv:r,additionalData:n,tagLength:128},t,e);return{v:1,alg:`A256GCM`,iv:Pt(r),ciphertext:Pt(new Uint8Array(i))}}async function Bt(e,t,n){if(!e||e.v!==1||e.alg!==`A256GCM`)throw Error(`Invalid encrypted diagram.`);let r=Ft(e.iv);if(r.byteLength!==12)throw Error(`Invalid encrypted diagram.`);let i=Ft(e.ciphertext);if(i.byteLength<16)throw Error(`Invalid encrypted diagram.`);return new Uint8Array(await crypto.subtle.decrypt({name:`AES-GCM`,iv:r,additionalData:n,tagLength:128},t,i))}async function Vt(e,t,n){return zt(Ot.encode(JSON.stringify(e)),t,n)}async function Ht(e,t,n){return JSON.parse(kt.decode(await Bt(e,t,n)))}function Ut(e,t){if(!e||typeof e!=`object`||!r(e.type)||typeof e.source!=`string`||!e.options||typeof e.options!=`object`||Array.isArray(e.options))throw Error(`Invalid encrypted diagram.`);return{type:e.type,source:e.source,options:e.options,presentation:H(e.presentation),meta:V(t)}}async function Wt(e,t){if(typeof t!=`string`||t.length<8)throw Error(`Password must be at least 8 characters.`);let n=It(16),r={name:`PBKDF2`,hash:`SHA-256`,iterations:Dt,salt:Pt(n)};return{v:1,kdf:r,wrap:await zt(e,await Rt(t,{iterations:r.iterations,salt:n}),Mt)}}async function Gt(e,t){if(!e||e.v!==1||e.kdf?.name!==`PBKDF2`||e.kdf.hash!==`SHA-256`||!Number.isInteger(e.kdf.iterations)||e.kdf.iterations<1e5||e.kdf.iterations>5e6)throw Error(`Invalid encrypted diagram.`);let n=Ft(e.kdf.salt);if(n.byteLength!==16)throw Error(`Invalid encrypted diagram.`);let r=await Rt(t,{iterations:e.kdf.iterations,salt:n}),i=await Bt(e.wrap,r,Mt);if(i.byteLength!==32)throw Error(`Invalid encrypted diagram.`);return i}async function Kt(e,t,n){let r=Ut({type:e.type,source:e.source,options:e.options??{},presentation:e.presentation},e.meta),i=await Lt(t,[`encrypt`]);return{mode:`locked`,encryptedContent:await Vt({type:r.type,source:r.source,options:r.options,presentation:r.presentation},i,At),encryptedMetadata:await Vt(r.meta,i,jt),keyEnvelope:n}}async function qt(e,t){let n=It(32);return{bundleKey:n,payload:await Kt(e,n,await Wt(n,t))}}async function Jt(e,t){if(!e||e.mode!==`locked`)throw Error(`Invalid encrypted diagram.`);try{let n=await Gt(e.keyEnvelope,t),r=await Lt(n,[`decrypt`]),[i,a]=await Promise.all([Ht(e.encryptedContent,r,At),Ht(e.encryptedMetadata,r,jt)]);return{bundleKey:n,state:Ut(i,a)}}catch{throw Error(`Wrong password or damaged diagram.`)}}async function Yt(e,t,n){if(!(e instanceof Blob))throw Error(`Invalid rendered image.`);let r=await Lt(t,[`encrypt`]);return zt(new Uint8Array(await e.arrayBuffer()),r,Nt(n))}var Xt=`diagram.zip:renderer:v1`,Zt=2e4,Qt=Object.freeze({mermaid:Object.freeze({frameUrl:`https://mermaid.render.diagram.zip/index.html?v=1`}),bpmn:Object.freeze({frameUrl:`https://bpmn.render.diagram.zip/index.html?v=1`}),excalidraw:Object.freeze({frameUrl:`https://excalidraw.render.diagram.zip/index.html?v=3`})});Object.freeze(Object.keys(Qt));function $t(e){return e?.name===`AbortError`?e:new DOMException(`Render superseded.`,`AbortError`)}var en=class{constructor({documentObject:e=document,windowObject:t=window,frameUrl:n}={}){this.document=e,this.window=t,this.frameUrl=n,this.frame=null,this.ready=null,this.resolveReady=null,this.rejectReady=null,this.sequence=0,this.pending=new Map,this.handleMessage=e=>this.receive(e),this.window.addEventListener(`message`,this.handleMessage)}ensureFrame(){if(this.frame)return this.ready;let e=this.document.createElement(`iframe`);return e.title=`Diagram renderer`,e.setAttribute(`sandbox`,`allow-scripts`),e.setAttribute(`aria-hidden`,`true`),e.tabIndex=-1,Object.assign(e.style,{position:`fixed`,left:`-200vw`,top:`0`,width:`1280px`,height:`800px`,border:`0`,pointerEvents:`none`,visibility:`hidden`}),this.ready=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t}),e.addEventListener(`error`,()=>this.rejectReady?.(Error(`The client renderer could not be loaded.`)),{once:!0}),e.src=this.frameUrl,this.frame=e,this.document.body.append(e),this.ready}receive(e){if(!this.frame||e.source!==this.frame.contentWindow||e.data?.channel!==Xt)return;if(e.data.type===`ready`){this.resolveReady?.(),this.resolveReady=null,this.rejectReady=null;return}if(e.data.type!==`result`)return;let t=this.pending.get(e.data.requestId);t&&(this.pending.delete(e.data.requestId),t.finish(),e.data.ok&&typeof e.data.svg==`string`?t.resolve({body:e.data.svg,version:e.data.version,build:e.data.build,pipeline:e.data.pipeline,runtime:`client`}):t.reject(Error(e.data.error||`Client rendering failed.`)))}async render(e,t,n){let r;try{await Promise.race([this.ensureFrame(),new Promise((e,t)=>{r=setTimeout(()=>t(Error(`The client renderer timed out while loading.`)),Zt)})])}finally{clearTimeout(r)}if(n.aborted)throw $t(n.reason);let i=`${Date.now().toString(36)}-${++this.sequence}`;return new Promise((r,a)=>{let o=setTimeout(()=>{this.pending.delete(i),n.removeEventListener(`abort`,s),a(Error(`The client renderer timed out.`))},Zt),s=()=>{this.pending.delete(i),clearTimeout(o),a($t(n.reason))};n.addEventListener(`abort`,s,{once:!0}),this.pending.set(i,{resolve:r,reject:a,finish:()=>{clearTimeout(o),n.removeEventListener(`abort`,s)}}),this.frame.contentWindow.postMessage({channel:Xt,type:`render`,requestId:i,engine:e,source:t},`*`)})}},tn=new Map;function nn(e){let t=Qt[e];if(!t)return null;let n=tn.get(t.frameUrl);return n||(n=new en({frameUrl:t.frameUrl}),tn.set(t.frameUrl,n)),{id:e,runtime:`client`,render:({source:t},r)=>n.render(e,t,r)}}var rn=e((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.CHAR=` 1630+ +-----------------+`,d2:c,nwdiag:h,packetdiag:ee,rackdiag:g,bpmn:o.replaceAll(`Examine Situation`,`Bob Agent tests Tandem`).replaceAll(`Things OK?`,`Tests pass?`).replaceAll(`Notification Sent`,`Alice and Bob share tasks`).replaceAll(`Error Propagated`,`Alice Agent revises flow`).replaceAll(`Klaus`,`Alice`).replaceAll(`Walter`,`Bob`),bytefield:s,dbml:l,diagramsnet:u,erd:d,excalidraw:f,goat:p,nomnoml:m,pikchr:te,svgbob:_,symbolator:ne,tikz:re,umlet:ie,vega:ae,vegalite:v,wavedrom:oe,wireviz:se},st={plantuml:[`Tandem task sync`,`Alice and Bob coordinate shared tasks through their agents.`],mermaid:[`Shared task flow`,`A compact view of ideas moving through the Tandem team.`],graphviz:[`Tandem collaboration graph`,`People and agents converge on one synchronized task list.`],d2:[`Connection styles`,`D2 connection, arrowhead, shape, and animation syntax.`],c4plantuml:[`Tandem system context`,`The people and software systems surrounding Tandem.`],blockdiag:[`Tandem block flow`,`A block-oriented view of the shared task workflow.`],seqdiag:[`Tandem task sequence`,`A task moves from Alice to Bob and back through Tandem.`],actdiag:[`Tandem delivery activities`,`Work crosses Alice, Alice Agent, and Bob Agent lanes.`],nwdiag:[`Network layout`,`A sample network topology and its connected address ranges.`],packetdiag:[`TCP packet structure`,`The bit-level layout of a TCP packet header.`],rackdiag:[`Rack layout`,`A sample equipment rack rendered from rackdiag syntax.`],bpmn:[`Agent-assisted review`,`A BPMN review flow that branches on test results.`],bytefield:[`Byte field layout`,`A compact byte-oriented protocol field example.`],dbml:[`Database schema`,`Tables and relationships expressed with DBML.`],diagramsnet:[`Venn diagram`,`An overlapping-set example imported from diagrams.net.`],ditaa:[`Tandem ASCII architecture`,`An ASCII sketch becomes a rendered system diagram.`],erd:[`Data model`,`Entities and relationships in a small relational schema.`],excalidraw:[`Venn sketch`,`A hand-drawn overlapping-set diagram.`],goat:[`Component shapes`,`GoAT exercises boxes, connectors, curves, and symbols.`],nomnoml:[`Pirate class diagram`,`A playful class diagram demonstrating Nomnoml syntax.`],pikchr:[`Cardinal points`,`A diamond marks north, east, south, and west.`],structurizr:[`Tandem landscape`,`A software landscape linking people, agents, and Tandem.`],svgbob:[`Cloud network`,`An ASCII cloud connects clients and services.`],symbolator:[`Hardware component`,`A component symbol generated from interface declarations.`],tikz:[`Periodic table`,`A color-coded periodic table rendered with TikZ.`],umlet:[`UML example`,`A UMLet document rendered directly from its XML form.`],vega:[`Bar chart`,`A declarative bar chart expressed with Vega.`],vegalite:[`Discretizing scale`,`A Vega-Lite example comparing discretized values.`],wavedrom:[`Timing diagram`,`Digital signals and timing relationships rendered by WaveDrom.`],wireviz:[`Wiring harness`,`Connections, cables, and pins described with WireViz.`]};function ct(e){return ot[e]??at}function lt(e){let t=n.findIndex(t=>t.id===e),[r,i]=st[e]??[`Diagram example`,`A diagram.zip catalog example.`];return{type:e,source:ct(e),options:{},...le(Math.max(t,0),{title:r,description:i})}}function ut({search:e=globalThis.location?.search??``,coarsePointer:t=globalThis.matchMedia?.(`(pointer: coarse)`).matches??!1}={}){let n=new URLSearchParams(e).get(`editor`);return n===`textarea`||n===`monaco`?n:t?`textarea`:`monaco`}async function dt(e){let t=ut();try{return await ft(t,e)}catch(n){if(t===`textarea`)throw n;return console.warn(`Monaco Editor is unavailable; using the basic text editor.`,n),ft(`textarea`,e)}}async function ft(e,n){let r=await(e===`textarea`?await t(()=>import(`./textarea-editor-BTXrLHn4.js`),[]):await t(()=>import(`./monaco-editor-C7iaFm9N.js`),__vite__mapDeps([0,1,2,3,4,5]))).createEditor(n);return n.element.dataset.editorBackend=e,r}function pt(e,t,n,r,i){return e.set(t,{...r,type:t}),e.get(n)??i(n)}var mt=`/api/v1`,U=/^[A-Za-z0-9_-]{16}$/,ht=/^[A-Za-z0-9_-]{43}$/;function gt(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/,``)}function _t(){return gt(crypto.getRandomValues(new Uint8Array(32)))}function vt(e){return e.match(/^\/d\/([A-Za-z0-9_-]{16})\/?$/)?.[1]??null}function yt(e,t){if(!U.test(t))throw Error(`Invalid diagram alias.`);return new URL(`/d/${t}`,e).toString()}function bt(e,t,n){if(!ht.test(n))throw Error(`Invalid write capability.`);let r=new URL(yt(e,t));return r.hash=`w=${n}`,r.toString()}function xt(e,t,n=`svg`){if(!U.test(t))throw Error(`Invalid diagram alias.`);if(n!==`svg`&&n!==`png`)throw Error(`Invalid render format.`);return new URL(`${mt}/aliases/${t}/renders/${n}`,e).toString()}function St(e){return e.match(/^#w=([A-Za-z0-9_-]{43})$/)?.[1]??null}var Ct=class extends Error{constructor(e,{status:t,code:n}={}){super(e),this.status=t,this.code=n}};function wt(e){if(!e||typeof e!=`object`||typeof e.type!=`string`||typeof e.source!=`string`)throw Error(`Invalid diagram state.`);return{mode:`open`,diagram:{type:e.type,source:e.source,options:e.options??{},presentation:H(e.presentation)},metadata:V(e.meta)}}function Tt(e){if(!e||typeof e!=`object`||!U.test(e.aliasId)||!ht.test(e.contentId)||!ht.test(e.renderId)||!Number.isSafeInteger(e.revision)||e.mode!==`open`&&e.mode!==`locked`)throw new Ct(`The persistence service returned an invalid response.`,{code:`invalid_response`});let t={aliasId:e.aliasId,contentId:e.contentId,renderId:e.renderId,revision:e.revision,mode:e.mode,createdAt:e.createdAt,updatedAt:e.updatedAt};if(e.mode===`locked`){if(!e.encryptedContent||!e.encryptedMetadata||!e.keyEnvelope)throw new Ct(`The persistence service returned an invalid locked diagram.`,{code:`invalid_response`});return{...t,encryptedContent:e.encryptedContent,encryptedMetadata:e.encryptedMetadata,keyEnvelope:e.keyEnvelope}}if(!e.diagram||typeof e.diagram!=`object`)throw new Ct(`The persistence service returned an invalid open diagram.`,{code:`invalid_response`});return{...t,state:{type:e.diagram.type,source:e.diagram.source,options:e.diagram.options??{},presentation:H(e.diagram.presentation),meta:V(e.metadata)}}}var Et=class{constructor({fetchImpl:e=globalThis.fetch,origin:t=globalThis.location?.origin??`http://localhost`,apiPrefix:n=mt}={}){this.fetch=(...t)=>e(...t),this.origin=t,this.apiPrefix=n.replace(/\/$/,``)}url(e=``){return new URL(`${this.apiPrefix}/aliases${e}`,this.origin)}async request(e,t){let n=await this.fetch(e,t),r=await n.json().catch(()=>null);if(!n.ok)throw new Ct(r?.error?.message||`Persistence request failed with HTTP ${n.status}.`,{status:n.status,code:r?.error?.code});return Tt(r)}async errorFor(e){let t=await e.json().catch(()=>null);return new Ct(t?.error?.message||`Persistence request failed with HTTP ${e.status}.`,{status:e.status,code:t?.error?.code})}async createPayload(e){let t=_t();return{...await this.request(this.url(),{method:`POST`,headers:{Authorization:`Bearer ${t}`,"Content-Type":`application/json`},body:JSON.stringify(e)}),writeCapability:t}}createAlias(e){return this.createPayload(wt(e))}createLockedAlias(e){if(e?.mode!==`locked`)throw Error(`Invalid locked diagram payload.`);return this.createPayload(e)}getAlias(e){if(!U.test(e))throw Error(`Invalid diagram alias.`);return this.request(this.url(`/${e}`),{headers:{Accept:`application/json`}})}updatePayload(e,t,n,r){if(!U.test(e))throw Error(`Invalid diagram alias.`);if(!Number.isSafeInteger(n)||n<1)throw Error(`Invalid alias revision.`);if(!ht.test(r))throw Error(`Invalid write capability.`);return this.request(this.url(`/${e}`),{method:`PUT`,headers:{Accept:`application/json`,Authorization:`Bearer ${r}`,"Content-Type":`application/json`,"If-Match":`"${n}"`},body:JSON.stringify(t)})}updateAlias(e,t,n,r){return this.updatePayload(e,wt(t),n,r)}updateLockedAlias(e,t,n,r){if(t?.mode!==`locked`)throw Error(`Invalid locked diagram payload.`);return this.updatePayload(e,t,n,r)}async uploadRender({aliasId:e,renderId:t,revision:n,writeCapability:r,format:i,mode:a,render:o,renderer:s}){if(!U.test(e))throw Error(`Invalid diagram alias.`);if(!ht.test(t))throw Error(`Invalid render ID.`);if(!Number.isSafeInteger(n)||n<1)throw Error(`Invalid alias revision.`);if(!ht.test(r))throw Error(`Invalid write capability.`);if(i!==`svg`&&i!==`png`)throw Error(`Invalid render format.`);if(a!==`open`&&a!==`locked`)throw Error(`Invalid diagram mode.`);if(!s||!/^[a-z][a-z0-9-]{0,31}$/.test(s.unit)||typeof s.build!=`string`||!/^[A-Za-z0-9][A-Za-z0-9._@+-]{0,127}$/.test(s.build)||!Array.isArray(s.pipeline)||s.pipeline.length<1||s.pipeline.some(e=>!/^[a-z][a-z0-9-]{0,31}$/.test(e)))throw Error(`Invalid renderer identity.`);let c=a===`locked`?`application/json`:i===`svg`?`image/svg+xml`:`image/png`,l=a===`locked`?JSON.stringify(o):o,u=await this.fetch(this.url(`/${e}/renders/${i}`),{method:`PUT`,headers:{Authorization:`Bearer ${r}`,"Content-Type":c,"If-Match":`"${n}"`,"X-Render-Id":t,"X-Renderer-Unit":s.unit,"X-Renderer-Build":s.build,"X-Renderer-Pipeline":s.pipeline.join(`,`)},body:l});if(!u.ok)throw await this.errorFor(u)}async getEncryptedRender(e,t){if(!U.test(e))throw Error(`Invalid diagram alias.`);if(t!==`svg`&&t!==`png`)throw Error(`Invalid render format.`);let n=this.url(`/${e}/renders/${t}`);n.searchParams.set(`encrypted`,`1`);let r=await this.fetch(n,{headers:{Accept:`application/json`}});if(!r.ok)throw await this.errorFor(r);return r.json()}},Dt=6e5,Ot=new TextEncoder,kt=new TextDecoder,At=Ot.encode(`diagram.zip:content:v1`),jt=Ot.encode(`diagram.zip:metadata:v1`),Mt=Ot.encode(`diagram.zip:bundle-key:v1`);function Nt(e){if(e!==`svg`&&e!==`png`)throw Error(`Invalid render format.`);return Ot.encode(`diagram.zip:render:${e}:v1`)}function Pt(e){let t=``,n=32768;for(let r=0;r<e.length;r+=n)t+=String.fromCharCode(...e.subarray(r,r+n));return btoa(t).replaceAll(`+`,`-`).replaceAll(`/`,`_`).replace(/=+$/,``)}function Ft(e){if(typeof e!=`string`||!/^[A-Za-z0-9_-]+$/.test(e))throw Error(`Invalid encrypted diagram.`);let t=e.replaceAll(`-`,`+`).replaceAll(`_`,`/`),n=t+`=`.repeat((4-t.length%4)%4),r=atob(n),i=Uint8Array.from(r,e=>e.charCodeAt(0));if(Pt(i)!==e)throw Error(`Invalid encrypted diagram.`);return i}function It(e){return crypto.getRandomValues(new Uint8Array(e))}async function Lt(e,t){if(!(e instanceof Uint8Array)||e.byteLength!==32)throw Error(`Invalid diagram key.`);return crypto.subtle.importKey(`raw`,e,{name:`AES-GCM`},!1,t)}async function Rt(e,{iterations:t,salt:n}){if(typeof e!=`string`)throw Error(`Password is required.`);let r=await crypto.subtle.importKey(`raw`,Ot.encode(e),`PBKDF2`,!1,[`deriveKey`]);return crypto.subtle.deriveKey({name:`PBKDF2`,hash:`SHA-256`,iterations:t,salt:n},r,{name:`AES-GCM`,length:256},!1,[`encrypt`,`decrypt`])}async function zt(e,t,n){let r=It(12),i=await crypto.subtle.encrypt({name:`AES-GCM`,iv:r,additionalData:n,tagLength:128},t,e);return{v:1,alg:`A256GCM`,iv:Pt(r),ciphertext:Pt(new Uint8Array(i))}}async function Bt(e,t,n){if(!e||e.v!==1||e.alg!==`A256GCM`)throw Error(`Invalid encrypted diagram.`);let r=Ft(e.iv);if(r.byteLength!==12)throw Error(`Invalid encrypted diagram.`);let i=Ft(e.ciphertext);if(i.byteLength<16)throw Error(`Invalid encrypted diagram.`);return new Uint8Array(await crypto.subtle.decrypt({name:`AES-GCM`,iv:r,additionalData:n,tagLength:128},t,i))}async function Vt(e,t,n){return zt(Ot.encode(JSON.stringify(e)),t,n)}async function Ht(e,t,n){return JSON.parse(kt.decode(await Bt(e,t,n)))}function Ut(e,t){if(!e||typeof e!=`object`||!r(e.type)||typeof e.source!=`string`||!e.options||typeof e.options!=`object`||Array.isArray(e.options))throw Error(`Invalid encrypted diagram.`);return{type:e.type,source:e.source,options:e.options,presentation:H(e.presentation),meta:V(t)}}async function Wt(e,t){if(typeof t!=`string`||t.length<8)throw Error(`Password must be at least 8 characters.`);let n=It(16),r={name:`PBKDF2`,hash:`SHA-256`,iterations:Dt,salt:Pt(n)};return{v:1,kdf:r,wrap:await zt(e,await Rt(t,{iterations:r.iterations,salt:n}),Mt)}}async function Gt(e,t){if(!e||e.v!==1||e.kdf?.name!==`PBKDF2`||e.kdf.hash!==`SHA-256`||!Number.isInteger(e.kdf.iterations)||e.kdf.iterations<1e5||e.kdf.iterations>5e6)throw Error(`Invalid encrypted diagram.`);let n=Ft(e.kdf.salt);if(n.byteLength!==16)throw Error(`Invalid encrypted diagram.`);let r=await Rt(t,{iterations:e.kdf.iterations,salt:n}),i=await Bt(e.wrap,r,Mt);if(i.byteLength!==32)throw Error(`Invalid encrypted diagram.`);return i}async function Kt(e,t,n){let r=Ut({type:e.type,source:e.source,options:e.options??{},presentation:e.presentation},e.meta),i=await Lt(t,[`encrypt`]);return{mode:`locked`,encryptedContent:await Vt({type:r.type,source:r.source,options:r.options,presentation:r.presentation},i,At),encryptedMetadata:await Vt(r.meta,i,jt),keyEnvelope:n}}async function qt(e,t){let n=It(32);return{bundleKey:n,payload:await Kt(e,n,await Wt(n,t))}}async function Jt(e,t){if(!e||e.mode!==`locked`)throw Error(`Invalid encrypted diagram.`);try{let n=await Gt(e.keyEnvelope,t),r=await Lt(n,[`decrypt`]),[i,a]=await Promise.all([Ht(e.encryptedContent,r,At),Ht(e.encryptedMetadata,r,jt)]);return{bundleKey:n,state:Ut(i,a)}}catch{throw Error(`Wrong password or damaged diagram.`)}}async function Yt(e,t,n){if(!(e instanceof Blob))throw Error(`Invalid rendered image.`);let r=await Lt(t,[`encrypt`]);return zt(new Uint8Array(await e.arrayBuffer()),r,Nt(n))}var Xt=`diagram.zip:renderer:v1`,Zt=2e4,Qt=Object.freeze({mermaid:Object.freeze({frameUrl:`https://mermaid.render.diagram.zip/index.html?v=1`}),bpmn:Object.freeze({frameUrl:`https://bpmn.render.diagram.zip/index.html?v=1`}),excalidraw:Object.freeze({frameUrl:`https://excalidraw.render.diagram.zip/index.html?v=3`}),diagramsnet:Object.freeze({frameUrl:`https://diagramsnet.render.diagram.zip/index.html?v=1`})});Object.freeze(Object.keys(Qt));function $t(e){return e?.name===`AbortError`?e:new DOMException(`Render superseded.`,`AbortError`)}var en=class{constructor({documentObject:e=document,windowObject:t=window,frameUrl:n}={}){this.document=e,this.window=t,this.frameUrl=n,this.frame=null,this.ready=null,this.resolveReady=null,this.rejectReady=null,this.sequence=0,this.pending=new Map,this.handleMessage=e=>this.receive(e),this.window.addEventListener(`message`,this.handleMessage)}ensureFrame(){if(this.frame)return this.ready;let e=this.document.createElement(`iframe`);return e.title=`Diagram renderer`,e.setAttribute(`sandbox`,`allow-scripts`),e.setAttribute(`aria-hidden`,`true`),e.tabIndex=-1,Object.assign(e.style,{position:`fixed`,left:`-200vw`,top:`0`,width:`1280px`,height:`800px`,border:`0`,pointerEvents:`none`,visibility:`hidden`}),this.ready=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t}),e.addEventListener(`error`,()=>this.rejectReady?.(Error(`The client renderer could not be loaded.`)),{once:!0}),e.src=this.frameUrl,this.frame=e,this.document.body.append(e),this.ready}receive(e){if(!this.frame||e.source!==this.frame.contentWindow||e.data?.channel!==Xt)return;if(e.data.type===`ready`){this.resolveReady?.(),this.resolveReady=null,this.rejectReady=null;return}if(e.data.type!==`result`)return;let t=this.pending.get(e.data.requestId);t&&(this.pending.delete(e.data.requestId),t.finish(),e.data.ok&&typeof e.data.svg==`string`?t.resolve({body:e.data.svg,version:e.data.version,build:e.data.build,pipeline:e.data.pipeline,runtime:`client`}):t.reject(Error(e.data.error||`Client rendering failed.`)))}async render(e,t,n){let r;try{await Promise.race([this.ensureFrame(),new Promise((e,t)=>{r=setTimeout(()=>t(Error(`The client renderer timed out while loading.`)),Zt)})])}finally{clearTimeout(r)}if(n.aborted)throw $t(n.reason);let i=`${Date.now().toString(36)}-${++this.sequence}`;return new Promise((r,a)=>{let o=setTimeout(()=>{this.pending.delete(i),n.removeEventListener(`abort`,s),a(Error(`The client renderer timed out.`))},Zt),s=()=>{this.pending.delete(i),clearTimeout(o),a($t(n.reason))};n.addEventListener(`abort`,s,{once:!0}),this.pending.set(i,{resolve:r,reject:a,finish:()=>{clearTimeout(o),n.removeEventListener(`abort`,s)}}),this.frame.contentWindow.postMessage({channel:Xt,type:`render`,requestId:i,engine:e,source:t},`*`)})}},tn=new Map;function nn(e){let t=Qt[e];if(!t)return null;let n=tn.get(t.frameUrl);return n||(n=new en({frameUrl:t.frameUrl}),tn.set(t.frameUrl,n)),{id:e,runtime:`client`,render:({source:t},r)=>n.render(e,t,r)}}var rn=e((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.CHAR=` 16311631 \r -퟿-�𐀀-􏿿`,e.S=` \r16321632 `,e.NAME_START_CHAR=`:A-Z_a-zÀ-ÖØ-öø-˿Ͱ-ͽͿ-῿‌‍⁰-↏Ⰰ-⿯、-퟿豈-﷏ﷰ-�𐀀-󯿿`,e.NAME_CHAR=`-`+e.NAME_START_CHAR+`.0-9·̀-ͯ‿-⁀`,e.CHAR_RE=RegExp(`^[`+e.CHAR+`]$`,`u`),e.S_RE=RegExp(`^[`+e.S+`]+$`,`u`),e.NAME_START_CHAR_RE=RegExp(`^[`+e.NAME_START_CHAR+`]$`,`u`),e.NAME_CHAR_RE=RegExp(`^[`+e.NAME_CHAR+`]$`,`u`),e.NAME_RE=RegExp(`^[`+e.NAME_START_CHAR+`][`+e.NAME_CHAR+`]*$`,`u`),e.NMTOKEN_RE=RegExp(`^[`+e.NAME_CHAR+`]+$`,`u`);var t=9,n=10,r=13,i=32;e.S_LIST=[i,n,r,t];function a(e){return e>=i&&e<=55295||e===n||e===r||e===t||e>=57344&&e<=65533||e>=65536&&e<=1114111}e.isChar=a;function o(e){return e===i||e===n||e===r||e===t}e.isS=o;function s(e){return e>=65&&e<=90||e>=97&&e<=122||e===58||e===95||e===8204||e===8205||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=767||e>=880&&e<=893||e>=895&&e<=8191||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}e.isNameStartChar=s;function c(e){return s(e)||e>=48&&e<=57||e===45||e===46||e===183||e>=768&&e<=879||e>=8255&&e<=8256}e.isNameChar=c})),an=e((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.CHAR=`-퟿-�𐀀-􏿿`,e.RESTRICTED_CHAR=`-\b\v\f--„†-Ÿ`,e.S=` \r16331633 `,e.NAME_START_CHAR=`:A-Z_a-zÀ-ÖØ-öø-˿Ͱ-ͽͿ-῿‌‍⁰-↏Ⰰ-⿯、-퟿豈-﷏ﷰ-�𐀀-󯿿`,e.NAME_CHAR=`-`+e.NAME_START_CHAR+`.0-9·̀-ͯ‿-⁀`,e.CHAR_RE=RegExp(`^[`+e.CHAR+`]$`,`u`),e.RESTRICTED_CHAR_RE=RegExp(`^[`+e.RESTRICTED_CHAR+`]$`,`u`),e.S_RE=RegExp(`^[`+e.S+`]+$`,`u`),e.NAME_START_CHAR_RE=RegExp(`^[`+e.NAME_START_CHAR+`]$`,`u`),e.NAME_CHAR_RE=RegExp(`^[`+e.NAME_CHAR+`]$`,`u`),e.NAME_RE=RegExp(`^[`+e.NAME_START_CHAR+`][`+e.NAME_CHAR+`]*$`,`u`),e.NMTOKEN_RE=RegExp(`^[`+e.NAME_CHAR+`]+$`,`u`);var t=9,n=10,r=13,i=32;e.S_LIST=[i,n,r,t];function a(e){return e>=1&&e<=55295||e>=57344&&e<=65533||e>=65536&&e<=1114111}e.isChar=a;function o(e){return e>=1&&e<=8||e===11||e===12||e>=14&&e<=31||e>=127&&e<=132||e>=134&&e<=159}e.isRestrictedChar=o;function s(e){return e===9||e===10||e===13||e>31&&e<127||e===133||e>159&&e<=55295||e>=57344&&e<=65533||e>=65536&&e<=1114111}e.isCharAndNotRestricted=s;function c(e){return e===i||e===n||e===r||e===t}e.isS=c;function l(e){return e>=65&&e<=90||e>=97&&e<=122||e===58||e===95||e===8204||e===8205||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=767||e>=880&&e<=893||e>=895&&e<=8191||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}e.isNameStartChar=l;function u(e){return l(e)||e>=48&&e<=57||e===45||e===46||e===183||e>=768&&e<=879||e>=8255&&e<=8256}e.isNameChar=u})),on=e((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.NC_NAME_START_CHAR=`A-Z_a-zÀ-ÖØ-öø-˿Ͱ-ͽͿ-῿‌-‍⁰-↏Ⰰ-⿯、-퟿豈-﷏ﷰ-�𐀀-󯿿`,e.NC_NAME_CHAR=`-`+e.NC_NAME_START_CHAR+`.0-9·̀-ͯ‿-⁀`,e.NC_NAME_START_CHAR_RE=RegExp(`^[`+e.NC_NAME_START_CHAR+`]$`,`u`),e.NC_NAME_CHAR_RE=RegExp(`^[`+e.NC_NAME_CHAR+`]$`,`u`),e.NC_NAME_RE=RegExp(`^[`+e.NC_NAME_START_CHAR+`][`+e.NC_NAME_CHAR+`]*$`,`u`);function t(e){return e>=65&&e<=90||e===95||e>=97&&e<=122||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=767||e>=880&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}e.isNCNameStartChar=t;function n(e){return t(e)||e===45||e===46||e>=48&&e<=57||e===183||e>=768&&e<=879||e>=8255&&e<=8256}e.isNCNameChar=n})),sn=e((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.SaxesParser=e.EVENTS=void 0;var t=rn(),n=an(),r=on(),i=t.isS,a=t.isChar,o=t.isNameStartChar,s=t.isNameChar,c=t.S_LIST,l=t.NAME_RE,u=n.isChar,d=r.isNCNameStartChar,f=r.isNCNameChar,p=r.NC_NAME_RE,m=`http://www.w3.org/XML/1998/namespace`,h=`http://www.w3.org/2000/xmlns/`,ee={__proto__:null,xml:m,xmlns:h},te={__proto__:null,amp:`&`,gt:`>`,lt:`<`,quot:`"`,apos:`'`},g=-1,_=-2,ne=0,re=1,ie=2,ae=3,v=4,oe=5,se=6,ce=7,le=8,ue=9,de=10,fe=11,pe=12,y=13,me=14,he=15,ge=16,_e=17,ve=18,ye=19,be=20,xe=21,Se=22,Ce=23,we=24,Te=25,Ee=26,De=27,Oe=28,ke=29,Ae=30,je=31,Me=32,b=33,Ne=34,Pe=35,x=36,S=37,C=38,w=39,Fe=40,Ie=41,Le=42,Re=43,ze=44,Be=9,T=10,Ve=13,He=32,Ue=33,We=34,Ge=38,Ke=39,qe=45,E=47,Je=59,D=60,O=61,k=62,A=63,j=91,M=93,N=133,P=8232,F=e=>e===We||e===Ke,Ye=[We,Ke],Xe=[...Ye,j,k],Ze=[...Ye,D,M],Qe=[O,A,...c],I=[...c,k,Ge,D];function $e(e,t,n){switch(t){case`xml`:n!==m&&e.fail(`xml prefix must be bound to ${m}.`);break;case`xmlns`:n!==h&&e.fail(`xmlns prefix must be bound to ${h}.`)}switch(n){case h:e.fail(t===``?`the default namespace may not be set to ${n}.`:`may not assign a prefix (even "xmlns") to the URI \