1<script> 2 import { Base64 } from 'js-base64'; 3 import moment from 'moment'; 4 import { codeStore } from '../code-store.js'; 5 6 const getBase64SVG = () => { 7 const container = document.getElementById('container'); 8 const svg = container.innerHTML.replaceAll('<br>', '<br/>'); 9 return Base64.encode(svg); 10 }; 11 12 const exportImage = (event, exporter) => { 13 const canvas = document.createElement('canvas'); 14 const svg = document.querySelector('#container svg'); 15 const box = svg.getBoundingClientRect(); 16 canvas.width = box.width; 17 canvas.height = box.height; 18 if (imagemodeselected === 'width') { 19 const ratio = box.height / box.width; 20 canvas.width = userimagewidth; 21 canvas.height = userimagewidth * ratio; 22 } else if (imagemodeselected === 'height') { 23 const ratio = box.width / box.height; 24 canvas.width = userimageheight * ratio; 25 canvas.height = userimageheight; 26 } 27 const context = canvas.getContext('2d'); 28 context.fillStyle = 'white'; 29 context.fillRect(0, 0, canvas.width, canvas.height); 30 31 const image = new Image(); 32 image.onload = exporter(canvas, context, image); 33 34 console.log('SVG', getBase64SVG()); 35 image.src = `data:image/svg+xml;base64,${getBase64SVG()}`; 36 event.stopPropagation(); 37 event.preventDefault(); 38 }; 39 40 const downloadImage = (canvas, context, image) => { 41 return () => { 42 context.drawImage(image, 0, 0, canvas.width, canvas.height); 43 44 const a = document.createElement('a'); 45 a.download = `mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.png`; 46 a.href = canvas 47 .toDataURL('image/png') 48 .replace('image/png', 'image/octet-stream'); 49 a.click(); 50 }; 51 }; 52 53 const isClipboardAvailable = () => { 54 return window.hasOwnProperty('ClipboardItem'); 55 }; 56 57 const clipboardCopy = (canvas, context, image) => { 58 return () => { 59 context.drawImage(image, 0, 0, canvas.width, canvas.height); 60 61 canvas.toBlob((blob) => { 62 try { 63 navigator.clipboard.write([ 64 new ClipboardItem({ 65 [blob.type]: blob, 66 }), 67 ]); 68 } catch (error) { 69 console.error(error); 70 } 71 }); 72 }; 73 }; 74 75 const onCopyClipboard = (event) => { 76 exportImage(event, clipboardCopy); 77 }; 78 79 const onDownloadPNG = (event) => { 80 exportImage(event, downloadImage); 81 }; 82 83 const onDownloadSVG = (event) => { 84 console.log('event', event.target); 85 event.target.href = `data:image/svg+xml;base64,${getBase64SVG()}`; 86 event.target.download = `mermaid-diagram-${moment().format( 87 'YYYYMMDDHHmmss' 88 )}.svg`; 89 console.log('event', event); 90 }; 91 92 const onCopyMarkdown = (event) => { 93 event.target.select(); 94 document.execCommand('Copy'); 95 }; 96 97 let url; 98 let b64Code; 99 let iUrl; 100 let svgUrl; 101 let mdCode; 102 let imagemodeselected = 'auto'; 103 let userimagewidth = 1920; 104 let userimageheight = 1080; 105 106 const unsubscribe = codeStore.subscribe((state) => { 107 b64Code = Base64.encodeURI(JSON.stringify(state)); 108 url = `${window.location.pathname.split('#')[0]}#/view/${b64Code}`; 109 iUrl = `https://mermaid.ink/img/${b64Code}`; 110 svgUrl = `https://mermaid.ink/svg/${b64Code}`; 111 mdCode = `[![](${iUrl})](${window.location.protocol}//${window.location.host}${window.location.pathname}#/edit/${b64Code})`; 112 }); 113</script> 114 115<style> 116 #links { 117 margin-bottom: 1rem; 118 padding-bottom: 0.5rem; 119 border-bottom: 1px solid lightgray; 120 } 121 #markdown { 122 padding: 7px; 123 font-family: monospace; 124 font-size: 14px; 125 width: 95%; 126 margin: 1rem 0; 127 border: 1px solid lightgray; 128 } 129 label[for='markdown'] { 130 cursor: pointer; 131 margin: 0 auto; 132 } 133 /* 134 @media (prefers-color-scheme: dark) { 135 label[for="markdown"] { 136 color: #d8d8d8; 137 } 138 } */ 139 .button-style { 140 background-color: #a2d9e2; 141 color: #33a2c4; 142 border-radius: 0.25rem; 143 padding: 0.5rem; 144 border: 1px solid #a2d9e2; 145 margin: 0.25rem; 146 } 147 .button-style:hover { 148 background-color: #fff; 149 color: #33a2c4; 150 border: 1px solid #33a2c4; 151 } 152 .button-style:focus { 153 outline: none; 154 } 155 .link-style { 156 text-decoration: none; 157 color: #33a2c4; 158 } 159 #copy-section { 160 padding-top: 1rem; 161 text-align: center; 162 } 163</style> 164 165<div id="links"> 166 {#if isClipboardAvailable()} 167 <button class="button-style"> 168 <a class="link-style" href={url} download="" on:click={onCopyClipboard}> 169 Copy Image 170 </a> 171 </button> 172 {/if} 173 <button class="button-style"> 174 <a class="link-style" href={url} download="" on:click={onDownloadPNG}> 175 Download PNG 176 </a> 177 </button> 178 <button class="button-style"> 179 <a class="link-style" href={url}>Link to view</a> 180 </button> 181 <button class="button-style"> 182 <a class="link-style" href={url} download="" on:click={onDownloadSVG}> 183 Download SVG 184 </a> 185 </button> 186 <button class="button-style"> 187 <a class="link-style" href={iUrl}>Link to Image</a> 188 </button> 189 <button class="button-style"> 190 <a class="link-style" href={svgUrl}>Link to SVG</a> 191 </button> 192 (markdown is base64 encoded for these urls) 193</div> 194<div id="copy-section"> 195 <label for="markdown" class="button-style">Copy Markdown</label> 196 <br /> 197 <input id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} /> 198</div> 199<p> 200 <label>PNG size:</label><br /> 201 <input 202 type="radio" 203 value="auto" 204 id="autosize" 205 bind:group={imagemodeselected} /> 206 <label for="autosize">auto</label><br /> 207 <input 208 type="radio" 209 value="width" 210 id="width-active" 211 bind:group={imagemodeselected} /> 212 <label for="width">width</label> 213 <input 214 id="width" 215 type="number" 216 min="3" 217 max="10000" 218 bind:value={userimagewidth} 219 disabled={imagemodeselected !== 'width'} /><br /> 220 <input 221 type="radio" 222 value="height" 223 id="height-active" 224 bind:group={imagemodeselected} /> 225 <label for="height">height</label> 226 <input 227 id="height" 228 type="number" 229 min="3" 230 max="10000" 231 bind:value={userimageheight} 232 disabled={imagemodeselected !== 'height'} /><br /> 233</p> 234