clone

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

style(diagram.zip): simplify dialog headers

Dami committed d213abe
commit
d213abe107df70ce72282d734f71fe08e2a73d92
parent
9558440aa656833e8d9ca31a2f44eb948abd4cae
author
Dami <dami.etoile@gmail.com>
6 files changed, +31 -34
Mdiagramzip/src/main.js14++++++------
Mdiagramzip/src/style.css35++++++++++++++----------------
Aserver/src/main/resources/web/diagramzip/assets/index-B2XsKz37.css1+
Rserver/src/main/resources/web/diagramzip/assets/index-CiEjbWbf.js → server/src/main/resources/web/diagramzip/assets/index-DimbzOnT.js10++++----
Dserver/src/main/resources/web/diagramzip/assets/index-DysJyYXu.css1-
Mserver/src/main/resources/web/diagramzip/index.html4++--
M diagramzip/src/main.js +7 -7
@@ -107,7 +107,7 @@ document.querySelector('#app').innerHTML = `
107107 <dialog id="details-dialog" aria-labelledby="details-title">108108 <form method="dialog" class="dialog-card">109109 <div class="dialog-header">110- <div><p class="dialog-eyebrow">Diagram</p><h1 id="details-title">Add some context.</h1></div>110+ <h1 id="details-title">Diagram details</h1>111111 <button class="icon-button" value="cancel" aria-label="Close">×</button>112112 </div>113113 <div class="details-fields">
@@ -157,7 +157,7 @@ document.querySelector('#app').innerHTML = `
157157 <dialog id="share-dialog" aria-labelledby="share-title">158158 <form method="dialog" class="dialog-card">159159 <div class="dialog-header">160- <div><p class="dialog-eyebrow">Share</p><h1 id="share-title">Take the diagram with you.</h1></div>160+ <h1 id="share-title">Share diagram</h1>161161 <button class="icon-button" value="cancel" aria-label="Close">×</button>162162 </div>163163 <p class="share-status" id="share-status"></p>
@@ -176,7 +176,7 @@ document.querySelector('#app').innerHTML = `
176176 <dialog id="conflict-dialog" aria-labelledby="conflict-title">177177 <form method="dialog" class="dialog-card">178178 <div class="dialog-header">179- <div><p class="dialog-eyebrow">Save conflict</p><h1 id="conflict-title">This diagram changed elsewhere.</h1></div>179+ <h1 id="conflict-title">Save conflict</h1>180180 <button class="icon-button" value="cancel" aria-label="Close">×</button>181181 </div>182182 <p class="dialog-copy">Reload the saved version, or keep your work by saving it as a new diagram.</p>
@@ -190,7 +190,7 @@ document.querySelector('#app').innerHTML = `
190190 <dialog id="password-dialog" aria-labelledby="password-title">191191 <form class="dialog-card" id="password-form">192192 <div class="dialog-header">193- <div><p class="dialog-eyebrow">Encrypted diagram</p><h1 id="password-title">Enter password.</h1></div>193+ <h1 id="password-title">Enter password</h1>194194 <button class="icon-button" id="password-cancel" type="button" aria-label="Close">×</button>195195 </div>196196 <p class="dialog-copy" id="password-copy"></p>
@@ -646,7 +646,7 @@ async function svgToPngBlob(svg) {
646646 647647 async function lockDiagram() {648648 const password = await askPassword({649- title: 'Lock this diagram.',649+ title: 'Lock diagram',650650 copy: 'The source, metadata, and saved render will be encrypted in this browser. The password cannot be recovered.',651651 submitLabel: 'Lock diagram',652652 confirm: true,
@@ -671,7 +671,7 @@ async function lockDiagram() {
671671 async function changeDiagramPassword() {672672 if (remote.mode !== 'locked' || !(remote.bundleKey instanceof Uint8Array)) return673673 const password = await askPassword({674- title: 'Change the password.',674+ title: 'Change password',675675 copy: 'Only the encrypted bundle key will be rewrapped. The diagram content does not need to be re-encrypted.',676676 submitLabel: 'Change password',677677 confirm: true,
@@ -699,7 +699,7 @@ function unlockDiagram() {
699699 async function promptToUnlock(alias) {700700 while (true) {701701 const password = await askPassword({702- title: 'Unlock this diagram.',702+ title: 'Unlock diagram',703703 copy: 'Decryption happens only in this browser. The password is never sent to diagram.zip.',704704 submitLabel: 'Unlock',705705 confirm: false,
M diagramzip/src/style.css +16 -19
A server/src/main/resources/web/diagramzip/assets/index-B2XsKz37.css +1 -0
@@ -0,0 +1 @@
1+:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--page:#fff;--surface:#fff;--surface-raised:#fff;--ink:#000;--muted:#666;--line:#ddd;--soft:#f3f3f3;--action-bg:#000;--action-ink:#fff;--danger:#333;--editor-active-line:#0000000b;--syntax-keyword:#111;--syntax-string:#333;--syntax-comment:#858585;--syntax-number:#444;--syntax-operator:#555;--syntax-variable:#222;--dot:#0000001c;--preview-bg:#fff;--shadow:0 22px 70px #0000001f;--font-sans:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:"SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;font-family:var(--font-sans);font-synthesis:none;text-rendering:optimizelegibility}*{box-sizing:border-box}html,body,#app{height:100%}body{background:var(--page);color:var(--ink);margin:0;overflow:hidden}.app-shell,dialog,.toast{-webkit-user-select:none;user-select:none}.cm-content,.share-options input,.details-fields input,.details-fields textarea,.details-fields select,.render-status[data-state=error]{-webkit-user-select:text;user-select:text}button,input,select,textarea{font:inherit}button,select,textarea{color:inherit}button{cursor:pointer}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.app-shell{grid-template-rows:58px minmax(0,1fr);height:100%;display:grid}.app-header{border-bottom:1px solid var(--line);background:color-mix(in srgb, var(--page), transparent 5%);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);z-index:10;justify-content:space-between;align-items:center;gap:20px;padding:0 14px 0 20px;display:flex}.brand{color:var(--ink);letter-spacing:-.025em;align-items:center;gap:10px;font-weight:780;text-decoration:none;display:inline-flex}.brand-mark{width:24px;height:24px;display:block}.header-meta{align-items:center;gap:9px;min-width:0;display:flex}.render-status{max-width:min(38vw,420px);color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.render-status[data-state=error]{color:var(--danger)}.render-status[data-state=loading]:before{content:"◌ "}.type-picker select,.primary-action,.secondary-action,.preview-toolbar button,.mobile-tabs button,.icon-button,.share-options button{border:1px solid var(--line);background:var(--surface);border-radius:10px;min-height:36px}.type-picker select{max-width:170px;padding:7px 30px 7px 11px}.primary-action{border-color:var(--action-bg);background:var(--action-bg);color:var(--action-ink);padding:7px 15px;font-weight:720}.secondary-action{border:1px solid var(--line);background:var(--surface);border-radius:10px;min-height:36px;padding:7px 12px;font-weight:680}.secondary-action:hover{background:var(--soft)}.workspace{grid-template-columns:minmax(340px,.82fr) minmax(440px,1.18fr);min-width:0;min-height:0;display:grid}.editor-panel,.preview-panel{min-width:0;min-height:0}.editor-panel{border-right:1px solid var(--line);background:var(--surface);overflow:hidden}#editor{height:100%}.cm-editor{color:var(--ink);font-size:14px}.cm-gutter{color:var(--muted)}.cm-selectionBackground{background:color-mix(in srgb, var(--ink), transparent 78%)!important}.preview-panel{background:var(--preview-bg)}.preview-stage{touch-action:none;background-image:radial-gradient(var(--dot) 1px, transparent 1px);cursor:grab;background-size:20px 20px;background-color:var(--render-background,var(--preview-bg));width:100%;height:100%;min-height:320px;position:relative;overflow:hidden}.preview-stage[data-dragging=true]{cursor:grabbing}#preview-image{transform-origin:0 0;-webkit-user-select:none;user-select:none;-webkit-user-drag:none;max-width:none;position:absolute;top:0;left:0}.preview-stage:has(#preview-image:not([hidden])) .preview-empty{display:none}.preview-empty{color:var(--muted);margin:0;position:absolute;inset:50% auto auto 50%;transform:translate(-50%,-50%)}.preview-toolbar{border:1px solid var(--line);background:color-mix(in srgb, var(--surface-raised), transparent 7%);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-radius:14px;gap:6px;padding:5px;display:flex;position:absolute;bottom:14px;left:14px}.preview-toolbar button{background:0 0;min-width:36px;padding:6px 9px}.preview-toolbar button:hover{background:var(--soft)}.minimap{border:1px solid var(--line);background:var(--render-background,var(--preview-bg));width:168px;height:108px;box-shadow:var(--shadow);cursor:crosshair;border-radius:12px;position:absolute;bottom:14px;right:14px;overflow:hidden}.minimap img{object-fit:contain;pointer-events:none;-webkit-user-select:none;user-select:none;width:100%;height:100%}#minimap-viewport{border:2px solid var(--ink);background:color-mix(in srgb, var(--ink), transparent 82%);pointer-events:none;min-width:4px;min-height:4px;position:absolute}.mobile-tabs{display:none}dialog{width:min(620px,100% - 28px);max-height:calc(100% - 28px);color:var(--ink);border:1px solid var(--line);background:var(--surface-raised);box-shadow:var(--shadow);border-radius:12px;padding:0}dialog::backdrop{background:color-mix(in srgb, var(--page), transparent 25%);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}.dialog-card{padding:20px}.dialog-header{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:12px;margin:-4px -4px 20px;padding:0 0 12px;display:flex}.dialog-header h1{letter-spacing:-.01em;margin:0;font-size:17px;font-weight:750;line-height:1.25}.icon-button{border-radius:6px;flex:none;width:32px;min-height:32px;padding:0;font-size:20px;line-height:1}.share-options{gap:18px;display:grid}.share-status,.dialog-copy{color:var(--muted);margin:-8px 0 20px;line-height:1.5}.share-options label>span{margin-bottom:7px;font-weight:700;display:block}.share-options label>div{grid-template-columns:minmax(0,1fr) auto;gap:7px;display:grid}.share-options input{min-width:0;height:40px;color:var(--ink);border:1px solid var(--line);background:var(--page);font:12px/1.4 var(--font-mono);border-radius:9px;padding:8px 10px}.share-options button{padding:7px 13px;font-weight:700}.share-options button:disabled{cursor:not-allowed;opacity:.42}.share-options small{color:var(--danger);margin-top:6px;display:block}.details-fields{gap:18px;display:grid}.details-fields label>span{margin-bottom:7px;font-weight:700;display:block}.details-fields input,.details-fields textarea{width:100%;color:var(--ink);border:1px solid var(--line);background:var(--page);resize:vertical;border-radius:10px;padding:10px 12px}.details-fields input{height:42px}.details-fields textarea{min-height:108px;line-height:1.5}.presentation-fields{border:1px solid var(--line);border-radius:12px;grid-template-columns:minmax(0,1fr) 120px auto;align-items:end;gap:12px;margin:2px 0 0;padding:16px;display:grid}.presentation-fields legend{padding:0 6px;font-weight:750}.presentation-fields label>span{margin-bottom:7px;font-weight:700;display:block}.presentation-fields select,.presentation-fields input[type=number]{border:1px solid var(--line);background:var(--page);border-radius:9px;width:100%;height:40px;padding:8px 10px}.number-field{position:relative}.number-field input{padding-right:34px!important}.number-field span{color:var(--muted);font-size:13px;position:absolute;top:10px;right:10px}.presentation-fields .checkbox-field{align-items:center;gap:8px;min-height:40px;padding:0 4px;display:flex}.presentation-fields .checkbox-field input{width:18px;height:18px;accent-color:var(--ink)}.presentation-fields .checkbox-field span{margin:0;display:inline}.privacy-fields{border:1px solid var(--line);border-radius:12px;justify-content:space-between;align-items:center;gap:18px;margin:2px 0 0;padding:16px;display:flex}.privacy-fields legend{padding:0 6px;font-weight:750}.privacy-fields strong{margin-bottom:4px;display:block}.privacy-fields p{color:var(--muted);margin:0;font-size:13px;line-height:1.45}.privacy-actions{flex-wrap:wrap;flex:none;justify-content:flex-end;gap:7px;display:flex}.form-error{min-height:20px;color:var(--danger);margin:12px 0 -4px;font-size:13px}.dialog-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:22px;display:flex}.conflict-actions{justify-content:space-between}.toast{z-index:30;background:var(--ink);color:var(--page);opacity:0;pointer-events:none;border-radius:999px;padding:9px 15px;font-size:13px;font-weight:740;transition:opacity .14s,transform .14s;position:fixed;top:72px;left:50%;transform:translate(-50%,-10px)}.toast[data-visible=true]{opacity:1;transform:translate(-50%)}:focus-visible{outline:2px solid var(--ink);outline-offset:2px}@media (width<=760px){.app-shell{grid-template-rows:58px 42px minmax(0,1fr)}.render-status{display:none}.render-status[data-state=error]{z-index:20;border:1px solid color-mix(in srgb, var(--danger), transparent 55%);background:var(--surface-raised);max-width:none;max-height:84px;box-shadow:var(--shadow);white-space:normal;border-radius:10px;padding:10px 12px;display:block;position:fixed;top:108px;left:10px;right:10px;overflow:auto}.app-header{padding-left:14px}.brand span:last-child{display:none}.header-meta{flex:1;justify-content:flex-end;gap:6px}.type-picker{flex:0 142px;min-width:0}.type-picker select{width:100%;max-width:142px}.mobile-tabs{border-bottom:1px solid var(--line);background:var(--page);grid-template-columns:1fr 1fr;gap:4px;padding:4px;display:grid}.mobile-tabs button{min-height:32px;color:var(--muted);background:0 0;border-color:#0000;font-size:13px;font-weight:700}.mobile-tabs button[aria-selected=true]{background:var(--soft);color:var(--ink)}.workspace{width:100%;display:block;overflow:hidden}.workspace[data-mobile-panel=editor] .preview-panel,.workspace[data-mobile-panel=preview] .editor-panel{display:none}.editor-panel,.preview-panel{border-right:0;width:100%;height:100%}.minimap{width:132px;height:88px}.presentation-fields{grid-template-columns:1fr 100px}.presentation-fields .checkbox-field{grid-column:1/-1}.privacy-fields{flex-direction:column;align-items:stretch}.privacy-actions{justify-content:flex-start}}@media (width<=390px){.app-header{gap:8px}.header-meta{gap:6px}.type-picker{flex-basis:108px}.type-picker select{max-width:108px}.primary-action,.secondary-action{padding-inline:9px}.preview-toolbar{bottom:10px;left:10px}.minimap{bottom:62px;right:10px}}
R server/src/main/resources/web/diagramzip/assets/index-CiEjbWbf.js → server/src/main/resources/web/diagramzip/assets/index-DimbzOnT.js +5 -5
@@ -1687,7 +1687,7 @@ Rel(bob, tandem, "Completes tasks")
16871687 <dialog id="details-dialog" aria-labelledby="details-title">16881688 <form method="dialog" class="dialog-card">16891689 <div class="dialog-header">1690- <div><p class="dialog-eyebrow">Diagram</p><h1 id="details-title">Add some context.</h1></div>1690+ <h1 id="details-title">Diagram details</h1>16911691 <button class="icon-button" value="cancel" aria-label="Close">×</button>16921692 </div>16931693 <div class="details-fields">
@@ -1737,7 +1737,7 @@ Rel(bob, tandem, "Completes tasks")
17371737 <dialog id="share-dialog" aria-labelledby="share-title">17381738 <form method="dialog" class="dialog-card">17391739 <div class="dialog-header">1740- <div><p class="dialog-eyebrow">Share</p><h1 id="share-title">Take the diagram with you.</h1></div>1740+ <h1 id="share-title">Share diagram</h1>17411741 <button class="icon-button" value="cancel" aria-label="Close">×</button>17421742 </div>17431743 <p class="share-status" id="share-status"></p>
@@ -1756,7 +1756,7 @@ Rel(bob, tandem, "Completes tasks")
17561756 <dialog id="conflict-dialog" aria-labelledby="conflict-title">17571757 <form method="dialog" class="dialog-card">17581758 <div class="dialog-header">1759- <div><p class="dialog-eyebrow">Save conflict</p><h1 id="conflict-title">This diagram changed elsewhere.</h1></div>1759+ <h1 id="conflict-title">Save conflict</h1>17601760 <button class="icon-button" value="cancel" aria-label="Close">×</button>17611761 </div>17621762 <p class="dialog-copy">Reload the saved version, or keep your work by saving it as a new diagram.</p>
@@ -1770,7 +1770,7 @@ Rel(bob, tandem, "Completes tasks")
17701770 <dialog id="password-dialog" aria-labelledby="password-title">17711771 <form class="dialog-card" id="password-form">17721772 <div class="dialog-header">1773- <div><p class="dialog-eyebrow">Encrypted diagram</p><h1 id="password-title">Enter password.</h1></div>1773+ <h1 id="password-title">Enter password</h1>17741774 <button class="icon-button" id="password-cancel" type="button" aria-label="Close">×</button>17751775 </div>17761776 <p class="dialog-copy" id="password-copy"></p>
@@ -1786,4 +1786,4 @@ Rel(bob, tandem, "Completes tasks")
17861786 </dialog>17871787 17881788 <div class="toast" id="toast" role="status" aria-live="polite"></div>1789-`;var lT=document.querySelector(`#diagram-type`),uT=document.querySelector(`#diagram-title`),dT=document.querySelector(`#diagram-description`),fT=document.querySelector(`#diagram-background`),pT=document.querySelector(`#diagram-padding`),mT=document.querySelector(`#diagram-frame`);for(let{id:e,label:t}of mx)lT.add(new Option(t,e));var hT=await TT(),gT=iw(hT.meta),_T=aw(hT.presentation);lT.value=hT.type,uT.value=gT.title,dT.value=gT.description,fT.value=_T.background,pT.value=String(_T.padding),mT.checked=_T.frame,$T(),aT=hT.type,cT.set(hT.type,hT);var vT=cd.define([{tag:K.keyword,color:`var(--syntax-keyword)`,fontWeight:`650`},{tag:K.string,color:`var(--syntax-string)`},{tag:[K.comment,K.lineComment,K.blockComment],color:`var(--syntax-comment)`,fontStyle:`italic`},{tag:[K.number,K.bool],color:`var(--syntax-number)`},{tag:[K.operator,K.punctuation],color:`var(--syntax-operator)`},{tag:[K.variableName,K.typeName],color:`var(--syntax-variable)`}]),yT=new V({state:k.create({doc:hT.source,extensions:[j_,tT.of(yx(hT.type)),fd(vT),V.theme({"&":{height:`100%`,backgroundColor:`transparent`},".cm-scroller":{fontFamily:`var(--font-mono)`,lineHeight:`1.65`},".cm-content":{padding:`28px 10px 80px`},".cm-gutters":{backgroundColor:`transparent`,border:`0`,paddingLeft:`12px`},".cm-activeLine, .cm-activeLineGutter":{backgroundColor:`var(--editor-active-line)`},".cm-cursor, .cm-dropCursor":{borderLeftColor:`var(--ink)`},"&.cm-focused":{outline:`none`}}),V.updateListener.of(e=>{e.docChanged&&!iT&&ST()})]}),parent:document.querySelector(`#editor`)}),bT=new Zw({stage:document.querySelector(`#preview-stage`),image:document.querySelector(`#preview-image`),status:document.querySelector(`.render-status`),minimap:document.querySelector(`#minimap`),minimapImage:document.querySelector(`#minimap-image`),minimapViewport:document.querySelector(`#minimap-viewport`)});lT.addEventListener(`change`,()=>{cT.set(aT,xT());let e=lT.value;ET(cT.get(e)??fw(e))}),document.querySelector(`#details`).addEventListener(`click`,()=>{document.querySelector(`#details-dialog`).showModal(),uT.focus()});for(let e of[uT,dT,fT,pT,mT])e.addEventListener(`input`,()=>{$T(),clearTimeout(wT.timeout),wT.timeout=setTimeout(wT,220)});document.querySelector(`#details-dialog`).addEventListener(`close`,wT),document.querySelector(`#lock-diagram`).addEventListener(`click`,FT),document.querySelector(`#change-password`).addEventListener(`click`,IT),document.querySelector(`#unlock-diagram`).addEventListener(`click`,LT),document.querySelector(`#save`).addEventListener(`click`,()=>AT()),document.querySelector(`#share`).addEventListener(`click`,DT),document.querySelector(`#share-save`).addEventListener(`click`,async()=>{await AT(),OT()}),document.querySelector(`.brand`).addEventListener(`click`,e=>{e.preventDefault(),confirm(`Start a new diagram?`)&&QT()}),document.querySelectorAll(`[data-preview-action]`).forEach(e=>{e.addEventListener(`click`,()=>{let t=e.dataset.previewAction;t===`zoom-out`&&bT.zoom(.8),t===`zoom-in`&&bT.zoom(1.25),t===`fit`&&bT.fit(),t===`one-to-one`&&bT.oneToOne()})}),document.querySelectorAll(`.mobile-tabs button`).forEach(e=>{e.addEventListener(`click`,()=>{document.querySelector(`.workspace`).dataset.mobilePanel=e.dataset.panel,document.querySelectorAll(`.mobile-tabs button`).forEach(t=>{t.setAttribute(`aria-selected`,String(t===e))}),e.dataset.panel===`preview`&&bT.fit()})}),document.querySelectorAll(`[data-copy]`).forEach(e=>{e.addEventListener(`click`,async()=>{let t=document.querySelector(`#${e.dataset.copy}`);t.value&&(await eE(t.value),$(`Copied`))})}),window.addEventListener(`popstate`,()=>location.reload()),window.addEventListener(`keydown`,e=>{(e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`s`&&(e.preventDefault(),AT())});function xT(){let e=Math.min(256,Math.max(0,Number.parseInt(pT.value,10)||0));return{type:lT.value,source:yT.state.doc.toString(),options:{},meta:iw({title:uT.value,description:dT.value}),presentation:aw({background:fT.value,padding:e,frame:mT.checked})}}function ST(e=1200){clearTimeout(rT),rT=setTimeout(CT,e)}function CT(e=!0){let t=xT();GT(t),Q.dirty=Q.keyEnvelopeDirty||Q.savedSnapshot!==HT(t,Q.mode),XT(),ZT(),e&&bT.render(t)}function wT(){clearTimeout(wT.timeout),CT()}async function TT(){let e=vw(location.pathname);if(e){let t=Sw(location.hash);t&&qT(e,t),location.hash&&history.replaceState(null,``,`${location.pathname}${location.search}`);try{let n=await nT.getAlias(e),r=t??JT(e);if(n.mode===`locked`){let e=await RT(n);return e?(YT(n,r,e.state,e.bundleKey),e.state):(sT=`A password is required to open this diagram.`,fw($C))}YT(n,r,n.state);let i=WT(`${$w}${e}`);return i?.revision===n.revision?i.state:n.state}catch(e){sT=e instanceof Cw?e.message:`This saved diagram could not be loaded.`}}location.hash&&history.replaceState(null,``,`${location.pathname}${location.search}`);let t=WT(Qw);return t?t.state:fw($C)}function ET(e,t=!0){if(!bx(e.type))throw Error(`Unsupported diagram type.`);let n=iw(e.meta),r=aw(e.presentation);iT=!0,aT=e.type,cT.set(e.type,{...e,meta:n,presentation:r}),lT.value=e.type,uT.value=n.title,dT.value=n.description,fT.value=r.background,pT.value=String(r.padding),mT.checked=r.frame,$T(),yT.dispatch({changes:{from:0,to:yT.state.doc.length,insert:e.source},effects:tT.reconfigure(yx(e.type))}),iT=!1,t?CT():bT.render(xT())}function DT(){CT(),OT(),document.querySelector(`#share-dialog`).showModal()}function OT(){let e=!!Q.aliasId,t=e&&Q.savedMode===`locked`,n=e?yw(location.origin,Q.aliasId):``,r=e&&Q.writeCapability?bw(location.origin,Q.aliasId,Q.writeCapability):``,i=t?null:Q.savedState,a=i?xw(location.origin,Q.aliasId,`svg`):``,o=document.querySelector(`#image-link`),s=document.querySelector(`#markdown-link`),c=i?.meta.title.trim().replace(/[\[\]\r\n]/g,` `)||`Diagram`;kT(`viewer-link`,n,`Save to create a read link`),kT(`editor-link`,r,e?`No write capability on this device`:`Save to create an edit link`),kT(`image-link`,a,t?`Encrypted diagrams cannot be embedded`:e?`Unavailable for this diagram`:`Save to create an image link`),kT(`markdown-link`,a?`![${c}](${a})`:``,t?`Encrypted diagrams cannot be embedded`:e?`Unavailable for this diagram`:`Save to create Markdown`),document.querySelector(`#editor-link-note`).textContent=r?`Anyone with this link can update the diagram.`:e?`This device has read-only access. Save a copy to create a new edit link.`:`The edit link is a bearer credential and appears after saving.`,document.querySelector(`#image-link-note`).textContent=t?`This diagram is encrypted in your browser. Image embeds are disabled because they cannot ask for its password.`:``,o.dataset.state=t?`unavailable`:`ready`,s.dataset.state=t?`unavailable`:`ready`;let l=document.querySelector(`#share-status`),u=document.querySelector(`#share-save`);u.hidden=e&&!Q.dirty,e?Q.dirty&&Q.writeCapability?(l.textContent=`These links show the last saved version. Save your changes before sharing.`,u.textContent=`Save changes`):Q.dirty?(l.textContent=`You are editing a read-only diagram. Save a copy to share your changes.`,u.textContent=`Save as new`):l.textContent=t?Q.writeCapability?`The read link asks for the password. The edit link also grants permission to save changes.`:`The read link asks for the password. This device has read-only access.`:Q.writeCapability?`Copy a read link, or share the edit capability carefully.`:`This diagram is read-only on this device.`:(l.textContent=`Save this diagram to create stable share links.`,u.textContent=`Save diagram`)}function kT(e,t,n){let r=document.querySelector(`#${e}`),i=document.querySelector(`[data-copy="${e}"]`);r.value=t,r.placeholder=t?``:n,i.disabled=!t}async function AT(){if(oT)return!1;CT(!1);let e=xT();if(Q.aliasId&&!Q.dirty)return $(`Already saved`),!0;oT=!0,XT();try{if(Q.aliasId&&Q.writeCapability)return YT(Q.mode===`locked`?await nT.updateLockedAlias(Q.aliasId,await MT(e),Q.revision,Q.writeCapability):await nT.updateAlias(Q.aliasId,e,Q.revision,Q.writeCapability),Q.writeCapability,e,Q.mode===`locked`?Q.bundleKey:null),KT(`${$w}${Q.aliasId}`),await NT(e),$(`Saved`),!0;let t=!!Q.aliasId;return await jT(e),$(t?`Saved as a new diagram`:`Saved`),!0}catch(t){return t instanceof Cw&&t.status===412&&Q.aliasId?await BT(e):($(t instanceof Error?t.message:`Could not save this diagram.`),!1)}finally{oT=!1,XT()}}async function jT(e){let t=Q.aliasId,n=t?`${$w}${t}`:Qw,r=Q.bundleKey,i=Q.mode===`locked`?await nT.createLockedAlias(await MT(e)):await nT.createAlias(e);YT(i,i.writeCapability,e,Q.mode===`locked`?r:null),qT(i.aliasId,i.writeCapability),KT(n),history.pushState(null,``,`/d/${i.aliasId}`),await NT(e)}async function MT(e){if(!(Q.bundleKey instanceof Uint8Array)||!Q.keyEnvelope)throw Error(`This diagram is missing its browser encryption key.`);return Q.savedMode===`locked`&&Q.savedSnapshot===HT(e,`locked`)&&Q.encryptedContent&&Q.encryptedMetadata?{mode:`locked`,encryptedContent:Q.encryptedContent,encryptedMetadata:Q.encryptedMetadata,keyEnvelope:Q.keyEnvelope}:Kw(e,Q.bundleKey,Q.keyEnvelope)}async function NT(e){if(!Q.aliasId||!Q.writeCapability)return;await bT.render(e);let t=bT.svgBlobFor(e);if(!t)return;let n=[[`svg`,t]];try{n.push([`png`,await PT(t)])}catch{}let r=n.map(async([e,t])=>{let n=Q.mode===`locked`?await Yw(t,Q.bundleKey,e):t;return nT.uploadRender({aliasId:Q.aliasId,renderId:Q.renderId,revision:Q.revision,writeCapability:Q.writeCapability,format:e,mode:Q.mode,render:n})});(await Promise.allSettled(r)).every(e=>e.status===`rejected`)&&bT.setStatus(`Saved; render cache unavailable`,`ready`)}async function PT(e){let t=URL.createObjectURL(e);try{let e=new Image;e.src=t,await e.decode();let n=Math.min(1,4096/Math.max(e.naturalWidth,e.naturalHeight)),r=Math.max(1,Math.round(e.naturalWidth*n)),i=Math.max(1,Math.round(e.naturalHeight*n)),a=document.createElement(`canvas`);a.width=r,a.height=i;let o=a.getContext(`2d`);if(!o)throw Error(`Canvas is unavailable.`);o.drawImage(e,0,0,r,i);let s=await new Promise(e=>a.toBlob(e,`image/png`));if(!s)throw Error(`Could not create a PNG render.`);return s}finally{URL.revokeObjectURL(t)}}async function FT(){let e=await zT({title:`Lock this diagram.`,copy:`The source, metadata, and saved render will be encrypted in this browser. The password cannot be recovered.`,submitLabel:`Lock diagram`,confirm:!0});if(e!==null)try{let{bundleKey:t,payload:n}=await qw(xT(),e);Q.mode=`locked`,Q.bundleKey=t,Q.keyEnvelope=n.keyEnvelope,Q.encryptedContent=n.encryptedContent,Q.encryptedMetadata=n.encryptedMetadata,Q.keyEnvelopeDirty=!0,KT(Q.aliasId?`${$w}${Q.aliasId}`:Qw),CT(!1),$(`Locked in this browser — save to persist`)}catch(e){$(e instanceof Error?e.message:`Could not lock this diagram.`)}}async function IT(){if(Q.mode!==`locked`||!(Q.bundleKey instanceof Uint8Array))return;let e=await zT({title:`Change the password.`,copy:`Only the encrypted bundle key will be rewrapped. The diagram content does not need to be re-encrypted.`,submitLabel:`Change password`,confirm:!0});if(e!==null)try{Q.keyEnvelope=await Ww(Q.bundleKey,e),Q.keyEnvelopeDirty=!0,CT(!1),$(`Password changed — save to persist`)}catch(e){$(e instanceof Error?e.message:`Could not change the password.`)}}function LT(){Q.mode===`locked`&&confirm(`Remove password protection? The next save will store the diagram in plaintext.`)&&(Q.mode=`open`,Q.keyEnvelopeDirty=!1,CT(!1),$(`Password removed — save to persist`))}async function RT(e){for(;;){let t=await zT({title:`Unlock this diagram.`,copy:`Decryption happens only in this browser. The password is never sent to diagram.zip.`,submitLabel:`Unlock`,confirm:!1});if(t===null)return null;try{return await Jw(e,t)}catch(e){$(e instanceof Error?e.message:`Could not unlock this diagram.`)}}}function zT({title:e,copy:t,submitLabel:n,confirm:r}){let i=document.querySelector(`#password-dialog`),a=document.querySelector(`#password-form`),o=document.querySelector(`#diagram-password`),s=document.querySelector(`#diagram-password-confirm`),c=document.querySelector(`#password-confirm-field`),l=document.querySelector(`#password-error`);return document.querySelector(`#password-title`).textContent=e,document.querySelector(`#password-copy`).textContent=t,document.querySelector(`#password-submit`).textContent=n,c.hidden=!r,o.autocomplete=r?`new-password`:`current-password`,o.value=``,s.value=``,l.textContent=``,new Promise(e=>{let t=!1,n=n=>{t||(t=!0,a.removeEventListener(`submit`,c),document.querySelector(`#password-cancel`).removeEventListener(`click`,u),i.removeEventListener(`cancel`,u),i.removeEventListener(`close`,d),e(n))},c=e=>{e.preventDefault();let t=o.value;if(r&&t.length<8){l.textContent=`Use at least 8 characters.`,o.focus();return}if(r&&t!==s.value){l.textContent=`The passwords do not match.`,s.focus();return}i.close(),n(t)},u=e=>{e.preventDefault(),i.close(),n(null)},d=()=>n(null);a.addEventListener(`submit`,c),document.querySelector(`#password-cancel`).addEventListener(`click`,u),i.addEventListener(`cancel`,u),i.addEventListener(`close`,d),i.showModal(),o.focus()})}async function BT(e){let t=await VT();if(t===`reload`){let e=Q.aliasId,t=Q.writeCapability;try{let n=await nT.getAlias(e),r=n.state,i=null;if(n.mode===`locked`){let e=await RT(n);if(!e)return!1;r=e.state,i=e.bundleKey}return YT(n,t,r,i),KT(`${$w}${e}`),ET(r,!1),XT(),$(`Reloaded saved diagram`),!0}catch(e){return $(e instanceof Error?e.message:`Could not reload this diagram.`),!1}}return t===`copy`&&(await jT(e),$(`Saved as a new diagram`),!0)}function VT(){let e=document.querySelector(`#conflict-dialog`);return e.returnValue=``,e.showModal(),new Promise(t=>{e.addEventListener(`close`,()=>t(e.returnValue),{once:!0})})}function HT(e,t=`open`){return JSON.stringify({mode:t,type:e.type,source:e.source,options:e.options??{},meta:iw(e.meta),presentation:aw(e.presentation)})}function UT(e){if(!e||typeof e!=`object`||!bx(e.type)||typeof e.source!=`string`)return null;try{return{type:e.type,source:e.source,options:e.options??{},meta:iw(e.meta),presentation:aw(e.presentation)}}catch{return null}}function WT(e){try{let t=JSON.parse(localStorage.getItem(e)),n=UT(t?.state);return n?{state:n,revision:t.revision??null}:null}catch{return null}}function GT(e){if(Q.mode===`locked`)return;let t=Q.aliasId?`${$w}${Q.aliasId}`:Qw;try{localStorage.setItem(t,JSON.stringify({state:e,revision:Q.revision}))}catch{}}function KT(e){try{localStorage.removeItem(e)}catch{}}function qT(e,t){try{localStorage.setItem(`${eT}${e}`,t)}catch{}}function JT(e){try{return Sw(`#w=${localStorage.getItem(`${eT}${e}`)??``}`)}catch{return null}}function YT(e,t,n,r=null){Q={aliasId:e.aliasId,contentId:e.contentId,renderId:e.renderId,revision:e.revision,mode:e.mode,savedMode:e.mode,writeCapability:t,savedState:n,savedSnapshot:HT(n,e.mode),bundleKey:r,keyEnvelope:e.mode===`locked`?e.keyEnvelope:null,encryptedContent:e.mode===`locked`?e.encryptedContent:null,encryptedMetadata:e.mode===`locked`?e.encryptedMetadata:null,keyEnvelopeDirty:!1,dirty:!1},ZT(),XT()}function XT(){let e=document.querySelector(`#save`);if(oT){e.textContent=`Saving…`,e.disabled=!0;return}if(Q.aliasId&&!Q.dirty){e.textContent=`Saved`,e.disabled=!0;return}e.textContent=Q.aliasId&&!Q.writeCapability?`Save copy`:`Save`,e.disabled=!1}function ZT(){let e=Q.mode===`locked`;document.querySelector(`#privacy-label`).textContent=e?`Password locked`:`Open`,document.querySelector(`#privacy-description`).textContent=e?`Source, metadata, and saved renders are encrypted in your browser. Password-locked diagrams cannot be embedded.`:`Anyone with the read link can open and embed this diagram.`,document.querySelector(`#lock-diagram`).hidden=e,document.querySelector(`#change-password`).hidden=!e,document.querySelector(`#unlock-diagram`).hidden=!e}function QT(){Q={aliasId:null,contentId:null,renderId:null,revision:null,mode:`open`,savedMode:null,writeCapability:null,savedState:null,savedSnapshot:null,bundleKey:null,keyEnvelope:null,encryptedContent:null,encryptedMetadata:null,keyEnvelopeDirty:!1,dirty:!0},cT.clear(),history.pushState(null,``,`/`),ET(fw($C))}function $T(){let e=uT.value.trim(),t=dT.value.trim();document.title=e?`${e} — diagram.zip`:`diagram.zip`,document.querySelector(`meta[name="description"]`).content=t||`Create diagrams from text and share them as a link.`}async function eE(e){if(navigator.clipboard)return navigator.clipboard.writeText(e);let t=document.createElement(`textarea`);t.value=e,document.body.append(t),t.select(),document.execCommand(`copy`),t.remove()}function $(e){let t=document.querySelector(`#toast`);t.textContent=e,t.dataset.visible=`true`,clearTimeout($.timeout),$.timeout=setTimeout(()=>delete t.dataset.visible,1800)}CT(),sT&&$(sT);\ No newline at end of file1789+`;var lT=document.querySelector(`#diagram-type`),uT=document.querySelector(`#diagram-title`),dT=document.querySelector(`#diagram-description`),fT=document.querySelector(`#diagram-background`),pT=document.querySelector(`#diagram-padding`),mT=document.querySelector(`#diagram-frame`);for(let{id:e,label:t}of mx)lT.add(new Option(t,e));var hT=await TT(),gT=iw(hT.meta),_T=aw(hT.presentation);lT.value=hT.type,uT.value=gT.title,dT.value=gT.description,fT.value=_T.background,pT.value=String(_T.padding),mT.checked=_T.frame,$T(),aT=hT.type,cT.set(hT.type,hT);var vT=cd.define([{tag:K.keyword,color:`var(--syntax-keyword)`,fontWeight:`650`},{tag:K.string,color:`var(--syntax-string)`},{tag:[K.comment,K.lineComment,K.blockComment],color:`var(--syntax-comment)`,fontStyle:`italic`},{tag:[K.number,K.bool],color:`var(--syntax-number)`},{tag:[K.operator,K.punctuation],color:`var(--syntax-operator)`},{tag:[K.variableName,K.typeName],color:`var(--syntax-variable)`}]),yT=new V({state:k.create({doc:hT.source,extensions:[j_,tT.of(yx(hT.type)),fd(vT),V.theme({"&":{height:`100%`,backgroundColor:`transparent`},".cm-scroller":{fontFamily:`var(--font-mono)`,lineHeight:`1.65`},".cm-content":{padding:`28px 10px 80px`},".cm-gutters":{backgroundColor:`transparent`,border:`0`,paddingLeft:`12px`},".cm-activeLine, .cm-activeLineGutter":{backgroundColor:`var(--editor-active-line)`},".cm-cursor, .cm-dropCursor":{borderLeftColor:`var(--ink)`},"&.cm-focused":{outline:`none`}}),V.updateListener.of(e=>{e.docChanged&&!iT&&ST()})]}),parent:document.querySelector(`#editor`)}),bT=new Zw({stage:document.querySelector(`#preview-stage`),image:document.querySelector(`#preview-image`),status:document.querySelector(`.render-status`),minimap:document.querySelector(`#minimap`),minimapImage:document.querySelector(`#minimap-image`),minimapViewport:document.querySelector(`#minimap-viewport`)});lT.addEventListener(`change`,()=>{cT.set(aT,xT());let e=lT.value;ET(cT.get(e)??fw(e))}),document.querySelector(`#details`).addEventListener(`click`,()=>{document.querySelector(`#details-dialog`).showModal(),uT.focus()});for(let e of[uT,dT,fT,pT,mT])e.addEventListener(`input`,()=>{$T(),clearTimeout(wT.timeout),wT.timeout=setTimeout(wT,220)});document.querySelector(`#details-dialog`).addEventListener(`close`,wT),document.querySelector(`#lock-diagram`).addEventListener(`click`,FT),document.querySelector(`#change-password`).addEventListener(`click`,IT),document.querySelector(`#unlock-diagram`).addEventListener(`click`,LT),document.querySelector(`#save`).addEventListener(`click`,()=>AT()),document.querySelector(`#share`).addEventListener(`click`,DT),document.querySelector(`#share-save`).addEventListener(`click`,async()=>{await AT(),OT()}),document.querySelector(`.brand`).addEventListener(`click`,e=>{e.preventDefault(),confirm(`Start a new diagram?`)&&QT()}),document.querySelectorAll(`[data-preview-action]`).forEach(e=>{e.addEventListener(`click`,()=>{let t=e.dataset.previewAction;t===`zoom-out`&&bT.zoom(.8),t===`zoom-in`&&bT.zoom(1.25),t===`fit`&&bT.fit(),t===`one-to-one`&&bT.oneToOne()})}),document.querySelectorAll(`.mobile-tabs button`).forEach(e=>{e.addEventListener(`click`,()=>{document.querySelector(`.workspace`).dataset.mobilePanel=e.dataset.panel,document.querySelectorAll(`.mobile-tabs button`).forEach(t=>{t.setAttribute(`aria-selected`,String(t===e))}),e.dataset.panel===`preview`&&bT.fit()})}),document.querySelectorAll(`[data-copy]`).forEach(e=>{e.addEventListener(`click`,async()=>{let t=document.querySelector(`#${e.dataset.copy}`);t.value&&(await eE(t.value),$(`Copied`))})}),window.addEventListener(`popstate`,()=>location.reload()),window.addEventListener(`keydown`,e=>{(e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`s`&&(e.preventDefault(),AT())});function xT(){let e=Math.min(256,Math.max(0,Number.parseInt(pT.value,10)||0));return{type:lT.value,source:yT.state.doc.toString(),options:{},meta:iw({title:uT.value,description:dT.value}),presentation:aw({background:fT.value,padding:e,frame:mT.checked})}}function ST(e=1200){clearTimeout(rT),rT=setTimeout(CT,e)}function CT(e=!0){let t=xT();GT(t),Q.dirty=Q.keyEnvelopeDirty||Q.savedSnapshot!==HT(t,Q.mode),XT(),ZT(),e&&bT.render(t)}function wT(){clearTimeout(wT.timeout),CT()}async function TT(){let e=vw(location.pathname);if(e){let t=Sw(location.hash);t&&qT(e,t),location.hash&&history.replaceState(null,``,`${location.pathname}${location.search}`);try{let n=await nT.getAlias(e),r=t??JT(e);if(n.mode===`locked`){let e=await RT(n);return e?(YT(n,r,e.state,e.bundleKey),e.state):(sT=`A password is required to open this diagram.`,fw($C))}YT(n,r,n.state);let i=WT(`${$w}${e}`);return i?.revision===n.revision?i.state:n.state}catch(e){sT=e instanceof Cw?e.message:`This saved diagram could not be loaded.`}}location.hash&&history.replaceState(null,``,`${location.pathname}${location.search}`);let t=WT(Qw);return t?t.state:fw($C)}function ET(e,t=!0){if(!bx(e.type))throw Error(`Unsupported diagram type.`);let n=iw(e.meta),r=aw(e.presentation);iT=!0,aT=e.type,cT.set(e.type,{...e,meta:n,presentation:r}),lT.value=e.type,uT.value=n.title,dT.value=n.description,fT.value=r.background,pT.value=String(r.padding),mT.checked=r.frame,$T(),yT.dispatch({changes:{from:0,to:yT.state.doc.length,insert:e.source},effects:tT.reconfigure(yx(e.type))}),iT=!1,t?CT():bT.render(xT())}function DT(){CT(),OT(),document.querySelector(`#share-dialog`).showModal()}function OT(){let e=!!Q.aliasId,t=e&&Q.savedMode===`locked`,n=e?yw(location.origin,Q.aliasId):``,r=e&&Q.writeCapability?bw(location.origin,Q.aliasId,Q.writeCapability):``,i=t?null:Q.savedState,a=i?xw(location.origin,Q.aliasId,`svg`):``,o=document.querySelector(`#image-link`),s=document.querySelector(`#markdown-link`),c=i?.meta.title.trim().replace(/[\[\]\r\n]/g,` `)||`Diagram`;kT(`viewer-link`,n,`Save to create a read link`),kT(`editor-link`,r,e?`No write capability on this device`:`Save to create an edit link`),kT(`image-link`,a,t?`Encrypted diagrams cannot be embedded`:e?`Unavailable for this diagram`:`Save to create an image link`),kT(`markdown-link`,a?`![${c}](${a})`:``,t?`Encrypted diagrams cannot be embedded`:e?`Unavailable for this diagram`:`Save to create Markdown`),document.querySelector(`#editor-link-note`).textContent=r?`Anyone with this link can update the diagram.`:e?`This device has read-only access. Save a copy to create a new edit link.`:`The edit link is a bearer credential and appears after saving.`,document.querySelector(`#image-link-note`).textContent=t?`This diagram is encrypted in your browser. Image embeds are disabled because they cannot ask for its password.`:``,o.dataset.state=t?`unavailable`:`ready`,s.dataset.state=t?`unavailable`:`ready`;let l=document.querySelector(`#share-status`),u=document.querySelector(`#share-save`);u.hidden=e&&!Q.dirty,e?Q.dirty&&Q.writeCapability?(l.textContent=`These links show the last saved version. Save your changes before sharing.`,u.textContent=`Save changes`):Q.dirty?(l.textContent=`You are editing a read-only diagram. Save a copy to share your changes.`,u.textContent=`Save as new`):l.textContent=t?Q.writeCapability?`The read link asks for the password. The edit link also grants permission to save changes.`:`The read link asks for the password. This device has read-only access.`:Q.writeCapability?`Copy a read link, or share the edit capability carefully.`:`This diagram is read-only on this device.`:(l.textContent=`Save this diagram to create stable share links.`,u.textContent=`Save diagram`)}function kT(e,t,n){let r=document.querySelector(`#${e}`),i=document.querySelector(`[data-copy="${e}"]`);r.value=t,r.placeholder=t?``:n,i.disabled=!t}async function AT(){if(oT)return!1;CT(!1);let e=xT();if(Q.aliasId&&!Q.dirty)return $(`Already saved`),!0;oT=!0,XT();try{if(Q.aliasId&&Q.writeCapability)return YT(Q.mode===`locked`?await nT.updateLockedAlias(Q.aliasId,await MT(e),Q.revision,Q.writeCapability):await nT.updateAlias(Q.aliasId,e,Q.revision,Q.writeCapability),Q.writeCapability,e,Q.mode===`locked`?Q.bundleKey:null),KT(`${$w}${Q.aliasId}`),await NT(e),$(`Saved`),!0;let t=!!Q.aliasId;return await jT(e),$(t?`Saved as a new diagram`:`Saved`),!0}catch(t){return t instanceof Cw&&t.status===412&&Q.aliasId?await BT(e):($(t instanceof Error?t.message:`Could not save this diagram.`),!1)}finally{oT=!1,XT()}}async function jT(e){let t=Q.aliasId,n=t?`${$w}${t}`:Qw,r=Q.bundleKey,i=Q.mode===`locked`?await nT.createLockedAlias(await MT(e)):await nT.createAlias(e);YT(i,i.writeCapability,e,Q.mode===`locked`?r:null),qT(i.aliasId,i.writeCapability),KT(n),history.pushState(null,``,`/d/${i.aliasId}`),await NT(e)}async function MT(e){if(!(Q.bundleKey instanceof Uint8Array)||!Q.keyEnvelope)throw Error(`This diagram is missing its browser encryption key.`);return Q.savedMode===`locked`&&Q.savedSnapshot===HT(e,`locked`)&&Q.encryptedContent&&Q.encryptedMetadata?{mode:`locked`,encryptedContent:Q.encryptedContent,encryptedMetadata:Q.encryptedMetadata,keyEnvelope:Q.keyEnvelope}:Kw(e,Q.bundleKey,Q.keyEnvelope)}async function NT(e){if(!Q.aliasId||!Q.writeCapability)return;await bT.render(e);let t=bT.svgBlobFor(e);if(!t)return;let n=[[`svg`,t]];try{n.push([`png`,await PT(t)])}catch{}let r=n.map(async([e,t])=>{let n=Q.mode===`locked`?await Yw(t,Q.bundleKey,e):t;return nT.uploadRender({aliasId:Q.aliasId,renderId:Q.renderId,revision:Q.revision,writeCapability:Q.writeCapability,format:e,mode:Q.mode,render:n})});(await Promise.allSettled(r)).every(e=>e.status===`rejected`)&&bT.setStatus(`Saved; render cache unavailable`,`ready`)}async function PT(e){let t=URL.createObjectURL(e);try{let e=new Image;e.src=t,await e.decode();let n=Math.min(1,4096/Math.max(e.naturalWidth,e.naturalHeight)),r=Math.max(1,Math.round(e.naturalWidth*n)),i=Math.max(1,Math.round(e.naturalHeight*n)),a=document.createElement(`canvas`);a.width=r,a.height=i;let o=a.getContext(`2d`);if(!o)throw Error(`Canvas is unavailable.`);o.drawImage(e,0,0,r,i);let s=await new Promise(e=>a.toBlob(e,`image/png`));if(!s)throw Error(`Could not create a PNG render.`);return s}finally{URL.revokeObjectURL(t)}}async function FT(){let e=await zT({title:`Lock diagram`,copy:`The source, metadata, and saved render will be encrypted in this browser. The password cannot be recovered.`,submitLabel:`Lock diagram`,confirm:!0});if(e!==null)try{let{bundleKey:t,payload:n}=await qw(xT(),e);Q.mode=`locked`,Q.bundleKey=t,Q.keyEnvelope=n.keyEnvelope,Q.encryptedContent=n.encryptedContent,Q.encryptedMetadata=n.encryptedMetadata,Q.keyEnvelopeDirty=!0,KT(Q.aliasId?`${$w}${Q.aliasId}`:Qw),CT(!1),$(`Locked in this browser — save to persist`)}catch(e){$(e instanceof Error?e.message:`Could not lock this diagram.`)}}async function IT(){if(Q.mode!==`locked`||!(Q.bundleKey instanceof Uint8Array))return;let e=await zT({title:`Change password`,copy:`Only the encrypted bundle key will be rewrapped. The diagram content does not need to be re-encrypted.`,submitLabel:`Change password`,confirm:!0});if(e!==null)try{Q.keyEnvelope=await Ww(Q.bundleKey,e),Q.keyEnvelopeDirty=!0,CT(!1),$(`Password changed — save to persist`)}catch(e){$(e instanceof Error?e.message:`Could not change the password.`)}}function LT(){Q.mode===`locked`&&confirm(`Remove password protection? The next save will store the diagram in plaintext.`)&&(Q.mode=`open`,Q.keyEnvelopeDirty=!1,CT(!1),$(`Password removed — save to persist`))}async function RT(e){for(;;){let t=await zT({title:`Unlock diagram`,copy:`Decryption happens only in this browser. The password is never sent to diagram.zip.`,submitLabel:`Unlock`,confirm:!1});if(t===null)return null;try{return await Jw(e,t)}catch(e){$(e instanceof Error?e.message:`Could not unlock this diagram.`)}}}function zT({title:e,copy:t,submitLabel:n,confirm:r}){let i=document.querySelector(`#password-dialog`),a=document.querySelector(`#password-form`),o=document.querySelector(`#diagram-password`),s=document.querySelector(`#diagram-password-confirm`),c=document.querySelector(`#password-confirm-field`),l=document.querySelector(`#password-error`);return document.querySelector(`#password-title`).textContent=e,document.querySelector(`#password-copy`).textContent=t,document.querySelector(`#password-submit`).textContent=n,c.hidden=!r,o.autocomplete=r?`new-password`:`current-password`,o.value=``,s.value=``,l.textContent=``,new Promise(e=>{let t=!1,n=n=>{t||(t=!0,a.removeEventListener(`submit`,c),document.querySelector(`#password-cancel`).removeEventListener(`click`,u),i.removeEventListener(`cancel`,u),i.removeEventListener(`close`,d),e(n))},c=e=>{e.preventDefault();let t=o.value;if(r&&t.length<8){l.textContent=`Use at least 8 characters.`,o.focus();return}if(r&&t!==s.value){l.textContent=`The passwords do not match.`,s.focus();return}i.close(),n(t)},u=e=>{e.preventDefault(),i.close(),n(null)},d=()=>n(null);a.addEventListener(`submit`,c),document.querySelector(`#password-cancel`).addEventListener(`click`,u),i.addEventListener(`cancel`,u),i.addEventListener(`close`,d),i.showModal(),o.focus()})}async function BT(e){let t=await VT();if(t===`reload`){let e=Q.aliasId,t=Q.writeCapability;try{let n=await nT.getAlias(e),r=n.state,i=null;if(n.mode===`locked`){let e=await RT(n);if(!e)return!1;r=e.state,i=e.bundleKey}return YT(n,t,r,i),KT(`${$w}${e}`),ET(r,!1),XT(),$(`Reloaded saved diagram`),!0}catch(e){return $(e instanceof Error?e.message:`Could not reload this diagram.`),!1}}return t===`copy`&&(await jT(e),$(`Saved as a new diagram`),!0)}function VT(){let e=document.querySelector(`#conflict-dialog`);return e.returnValue=``,e.showModal(),new Promise(t=>{e.addEventListener(`close`,()=>t(e.returnValue),{once:!0})})}function HT(e,t=`open`){return JSON.stringify({mode:t,type:e.type,source:e.source,options:e.options??{},meta:iw(e.meta),presentation:aw(e.presentation)})}function UT(e){if(!e||typeof e!=`object`||!bx(e.type)||typeof e.source!=`string`)return null;try{return{type:e.type,source:e.source,options:e.options??{},meta:iw(e.meta),presentation:aw(e.presentation)}}catch{return null}}function WT(e){try{let t=JSON.parse(localStorage.getItem(e)),n=UT(t?.state);return n?{state:n,revision:t.revision??null}:null}catch{return null}}function GT(e){if(Q.mode===`locked`)return;let t=Q.aliasId?`${$w}${Q.aliasId}`:Qw;try{localStorage.setItem(t,JSON.stringify({state:e,revision:Q.revision}))}catch{}}function KT(e){try{localStorage.removeItem(e)}catch{}}function qT(e,t){try{localStorage.setItem(`${eT}${e}`,t)}catch{}}function JT(e){try{return Sw(`#w=${localStorage.getItem(`${eT}${e}`)??``}`)}catch{return null}}function YT(e,t,n,r=null){Q={aliasId:e.aliasId,contentId:e.contentId,renderId:e.renderId,revision:e.revision,mode:e.mode,savedMode:e.mode,writeCapability:t,savedState:n,savedSnapshot:HT(n,e.mode),bundleKey:r,keyEnvelope:e.mode===`locked`?e.keyEnvelope:null,encryptedContent:e.mode===`locked`?e.encryptedContent:null,encryptedMetadata:e.mode===`locked`?e.encryptedMetadata:null,keyEnvelopeDirty:!1,dirty:!1},ZT(),XT()}function XT(){let e=document.querySelector(`#save`);if(oT){e.textContent=`Saving…`,e.disabled=!0;return}if(Q.aliasId&&!Q.dirty){e.textContent=`Saved`,e.disabled=!0;return}e.textContent=Q.aliasId&&!Q.writeCapability?`Save copy`:`Save`,e.disabled=!1}function ZT(){let e=Q.mode===`locked`;document.querySelector(`#privacy-label`).textContent=e?`Password locked`:`Open`,document.querySelector(`#privacy-description`).textContent=e?`Source, metadata, and saved renders are encrypted in your browser. Password-locked diagrams cannot be embedded.`:`Anyone with the read link can open and embed this diagram.`,document.querySelector(`#lock-diagram`).hidden=e,document.querySelector(`#change-password`).hidden=!e,document.querySelector(`#unlock-diagram`).hidden=!e}function QT(){Q={aliasId:null,contentId:null,renderId:null,revision:null,mode:`open`,savedMode:null,writeCapability:null,savedState:null,savedSnapshot:null,bundleKey:null,keyEnvelope:null,encryptedContent:null,encryptedMetadata:null,keyEnvelopeDirty:!1,dirty:!0},cT.clear(),history.pushState(null,``,`/`),ET(fw($C))}function $T(){let e=uT.value.trim(),t=dT.value.trim();document.title=e?`${e} — diagram.zip`:`diagram.zip`,document.querySelector(`meta[name="description"]`).content=t||`Create diagrams from text and share them as a link.`}async function eE(e){if(navigator.clipboard)return navigator.clipboard.writeText(e);let t=document.createElement(`textarea`);t.value=e,document.body.append(t),t.select(),document.execCommand(`copy`),t.remove()}function $(e){let t=document.querySelector(`#toast`);t.textContent=e,t.dataset.visible=`true`,clearTimeout($.timeout),$.timeout=setTimeout(()=>delete t.dataset.visible,1800)}CT(),sT&&$(sT);\ No newline at end of file
D server/src/main/resources/web/diagramzip/assets/index-DysJyYXu.css +0 -1
@@ -1 +0,0 @@
1-:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--page:#fff;--surface:#fff;--surface-raised:#fff;--ink:#000;--muted:#666;--line:#ddd;--soft:#f3f3f3;--action-bg:#000;--action-ink:#fff;--danger:#333;--editor-active-line:#0000000b;--syntax-keyword:#111;--syntax-string:#333;--syntax-comment:#858585;--syntax-number:#444;--syntax-operator:#555;--syntax-variable:#222;--dot:#0000001c;--preview-bg:#fff;--shadow:0 22px 70px #0000001f;--font-sans:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:"SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;font-family:var(--font-sans);font-synthesis:none;text-rendering:optimizelegibility}*{box-sizing:border-box}html,body,#app{height:100%}body{background:var(--page);color:var(--ink);margin:0;overflow:hidden}.app-shell,dialog,.toast{-webkit-user-select:none;user-select:none}.cm-content,.share-options input,.details-fields input,.details-fields textarea,.details-fields select,.render-status[data-state=error]{-webkit-user-select:text;user-select:text}button,input,select,textarea{font:inherit}button,select,textarea{color:inherit}button{cursor:pointer}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.app-shell{grid-template-rows:58px minmax(0,1fr);height:100%;display:grid}.app-header{border-bottom:1px solid var(--line);background:color-mix(in srgb, var(--page), transparent 5%);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);z-index:10;justify-content:space-between;align-items:center;gap:20px;padding:0 14px 0 20px;display:flex}.brand{color:var(--ink);letter-spacing:-.025em;align-items:center;gap:10px;font-weight:780;text-decoration:none;display:inline-flex}.brand-mark{width:24px;height:24px;display:block}.header-meta{align-items:center;gap:9px;min-width:0;display:flex}.render-status{max-width:min(38vw,420px);color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.render-status[data-state=error]{color:var(--danger)}.render-status[data-state=loading]:before{content:"◌ "}.type-picker select,.primary-action,.secondary-action,.preview-toolbar button,.mobile-tabs button,.icon-button,.share-options button{border:1px solid var(--line);background:var(--surface);border-radius:10px;min-height:36px}.type-picker select{max-width:170px;padding:7px 30px 7px 11px}.primary-action{border-color:var(--action-bg);background:var(--action-bg);color:var(--action-ink);padding:7px 15px;font-weight:720}.secondary-action{border:1px solid var(--line);background:var(--surface);border-radius:10px;min-height:36px;padding:7px 12px;font-weight:680}.secondary-action:hover{background:var(--soft)}.workspace{grid-template-columns:minmax(340px,.82fr) minmax(440px,1.18fr);min-width:0;min-height:0;display:grid}.editor-panel,.preview-panel{min-width:0;min-height:0}.editor-panel{border-right:1px solid var(--line);background:var(--surface);overflow:hidden}#editor{height:100%}.cm-editor{color:var(--ink);font-size:14px}.cm-gutter{color:var(--muted)}.cm-selectionBackground{background:color-mix(in srgb, var(--ink), transparent 78%)!important}.preview-panel{background:var(--preview-bg)}.preview-stage{touch-action:none;background-image:radial-gradient(var(--dot) 1px, transparent 1px);cursor:grab;background-size:20px 20px;background-color:var(--render-background,var(--preview-bg));width:100%;height:100%;min-height:320px;position:relative;overflow:hidden}.preview-stage[data-dragging=true]{cursor:grabbing}#preview-image{transform-origin:0 0;-webkit-user-select:none;user-select:none;-webkit-user-drag:none;max-width:none;position:absolute;top:0;left:0}.preview-stage:has(#preview-image:not([hidden])) .preview-empty{display:none}.preview-empty{color:var(--muted);margin:0;position:absolute;inset:50% auto auto 50%;transform:translate(-50%,-50%)}.preview-toolbar{border:1px solid var(--line);background:color-mix(in srgb, var(--surface-raised), transparent 7%);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border-radius:14px;gap:6px;padding:5px;display:flex;position:absolute;bottom:14px;left:14px}.preview-toolbar button{background:0 0;min-width:36px;padding:6px 9px}.preview-toolbar button:hover{background:var(--soft)}.minimap{border:1px solid var(--line);background:var(--render-background,var(--preview-bg));width:168px;height:108px;box-shadow:var(--shadow);cursor:crosshair;border-radius:12px;position:absolute;bottom:14px;right:14px;overflow:hidden}.minimap img{object-fit:contain;pointer-events:none;-webkit-user-select:none;user-select:none;width:100%;height:100%}#minimap-viewport{border:2px solid var(--ink);background:color-mix(in srgb, var(--ink), transparent 82%);pointer-events:none;min-width:4px;min-height:4px;position:absolute}.mobile-tabs{display:none}dialog{width:min(620px,100% - 28px);max-height:calc(100% - 28px);color:var(--ink);border:1px solid var(--line);background:var(--surface-raised);box-shadow:var(--shadow);border-radius:20px;padding:0}dialog::backdrop{background:color-mix(in srgb, var(--page), transparent 25%);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}.dialog-card{padding:24px}.dialog-header{justify-content:space-between;align-items:flex-start;gap:20px;margin-bottom:24px;display:flex}.dialog-eyebrow{color:var(--muted);letter-spacing:.12em;text-transform:uppercase;margin:0 0 5px;font-size:12px;font-weight:800}.dialog-header h1{letter-spacing:-.045em;margin:0;font-size:clamp(25px,5vw,38px);line-height:1.05}.icon-button{flex:none;width:38px;padding:0;font-size:24px}.share-options{gap:18px;display:grid}.share-status,.dialog-copy{color:var(--muted);margin:-8px 0 20px;line-height:1.5}.share-options label>span{margin-bottom:7px;font-weight:700;display:block}.share-options label>div{grid-template-columns:minmax(0,1fr) auto;gap:7px;display:grid}.share-options input{min-width:0;height:40px;color:var(--ink);border:1px solid var(--line);background:var(--page);font:12px/1.4 var(--font-mono);border-radius:9px;padding:8px 10px}.share-options button{padding:7px 13px;font-weight:700}.share-options button:disabled{cursor:not-allowed;opacity:.42}.share-options small{color:var(--danger);margin-top:6px;display:block}.details-fields{gap:18px;display:grid}.details-fields label>span{margin-bottom:7px;font-weight:700;display:block}.details-fields input,.details-fields textarea{width:100%;color:var(--ink);border:1px solid var(--line);background:var(--page);resize:vertical;border-radius:10px;padding:10px 12px}.details-fields input{height:42px}.details-fields textarea{min-height:108px;line-height:1.5}.presentation-fields{border:1px solid var(--line);border-radius:12px;grid-template-columns:minmax(0,1fr) 120px auto;align-items:end;gap:12px;margin:2px 0 0;padding:16px;display:grid}.presentation-fields legend{padding:0 6px;font-weight:750}.presentation-fields label>span{margin-bottom:7px;font-weight:700;display:block}.presentation-fields select,.presentation-fields input[type=number]{border:1px solid var(--line);background:var(--page);border-radius:9px;width:100%;height:40px;padding:8px 10px}.number-field{position:relative}.number-field input{padding-right:34px!important}.number-field span{color:var(--muted);font-size:13px;position:absolute;top:10px;right:10px}.presentation-fields .checkbox-field{align-items:center;gap:8px;min-height:40px;padding:0 4px;display:flex}.presentation-fields .checkbox-field input{width:18px;height:18px;accent-color:var(--ink)}.presentation-fields .checkbox-field span{margin:0;display:inline}.privacy-fields{border:1px solid var(--line);border-radius:12px;justify-content:space-between;align-items:center;gap:18px;margin:2px 0 0;padding:16px;display:flex}.privacy-fields legend{padding:0 6px;font-weight:750}.privacy-fields strong{margin-bottom:4px;display:block}.privacy-fields p{color:var(--muted);margin:0;font-size:13px;line-height:1.45}.privacy-actions{flex-wrap:wrap;flex:none;justify-content:flex-end;gap:7px;display:flex}.form-error{min-height:20px;color:var(--danger);margin:12px 0 -4px;font-size:13px}.dialog-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:22px;display:flex}.conflict-actions{justify-content:space-between}.toast{z-index:30;background:var(--ink);color:var(--page);opacity:0;pointer-events:none;border-radius:999px;padding:9px 15px;font-size:13px;font-weight:740;transition:opacity .14s,transform .14s;position:fixed;top:72px;left:50%;transform:translate(-50%,-10px)}.toast[data-visible=true]{opacity:1;transform:translate(-50%)}:focus-visible{outline:2px solid var(--ink);outline-offset:2px}@media (width<=760px){.app-shell{grid-template-rows:58px 42px minmax(0,1fr)}.render-status{display:none}.render-status[data-state=error]{z-index:20;border:1px solid color-mix(in srgb, var(--danger), transparent 55%);background:var(--surface-raised);max-width:none;max-height:84px;box-shadow:var(--shadow);white-space:normal;border-radius:10px;padding:10px 12px;display:block;position:fixed;top:108px;left:10px;right:10px;overflow:auto}.app-header{padding-left:14px}.brand span:last-child{display:none}.header-meta{flex:1;justify-content:flex-end;gap:6px}.type-picker{flex:0 142px;min-width:0}.type-picker select{width:100%;max-width:142px}.mobile-tabs{border-bottom:1px solid var(--line);background:var(--page);grid-template-columns:1fr 1fr;gap:4px;padding:4px;display:grid}.mobile-tabs button{min-height:32px;color:var(--muted);background:0 0;border-color:#0000;font-size:13px;font-weight:700}.mobile-tabs button[aria-selected=true]{background:var(--soft);color:var(--ink)}.workspace{width:100%;display:block;overflow:hidden}.workspace[data-mobile-panel=editor] .preview-panel,.workspace[data-mobile-panel=preview] .editor-panel{display:none}.editor-panel,.preview-panel{border-right:0;width:100%;height:100%}.minimap{width:132px;height:88px}.presentation-fields{grid-template-columns:1fr 100px}.presentation-fields .checkbox-field{grid-column:1/-1}.privacy-fields{flex-direction:column;align-items:stretch}.privacy-actions{justify-content:flex-start}}@media (width<=390px){.app-header{gap:8px}.header-meta{gap:6px}.type-picker{flex-basis:108px}.type-picker select{max-width:108px}.primary-action,.secondary-action{padding-inline:9px}.preview-toolbar{bottom:10px;left:10px}.minimap{bottom:62px;right:10px}}
M server/src/main/resources/web/diagramzip/index.html +2 -2