From 36bcd32ee30fd23116508b70bc89513bd4a5c8b4 Mon Sep 17 00:00:00 2001 From: Marek Skrobacki Date: Mon, 17 Jan 2022 20:47:54 +0000 Subject: [PATCH 1/4] Kroki support This commit adds support for generating links to be rendered with [Kroki](https://kroki.io) compatible renderer. Apart from supporting many other diagram formats, it directly benefits Mermaid editor because it generates much shorter URLs (due to the compression). Custom Kroki URL can be configured through environment variable if you want to use self-hosted instance of Kroki. --- README.md | 5 +++++ package.json | 1 + src/lib/components/actions.svelte | 28 +++++++++++++++++++++++++++- src/lib/util/env.ts | 1 + yarn.lock | 5 +++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ad25caee67..11f95a6ff8 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,11 @@ docker run --publish 8000:80 ghcr.io/mermaid-js/mermaid-live-editor When building, Set the Environment variable MERMAID_RENDERER_URL to the rendering service. Default is `https://mermaid.ink` +### To configure Kroki Instance URL + +When building, Set the Environment variable MERMAID_KROKI_RENDERER_URL to your Kroki instance. +Default is `https://kroki.io` + ### Development ```bash diff --git a/package.json b/package.json index e8ba77a56c..e97f1c2761 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "moment": "^2.29.1", "monaco-editor": "^0.31.1", "monaco-mermaid": "^1.0.1", + "pako": "1.0.10", "random-word-slugs": "^0.1.6" }, "lint-staged": { diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte index 742a1821de..59d2f2b350 100644 --- a/src/lib/components/actions.svelte +++ b/src/lib/components/actions.svelte @@ -2,10 +2,11 @@ import { browser } from '$app/env'; import Card from '$lib/components/card/card.svelte'; - import { rendererUrl } from '$lib/util/env'; + import { rendererUrl, krokiRendererUrl } from '$lib/util/env'; import { base64State, codeStore } from '$lib/util/state'; import { toBase64 } from 'js-base64'; import moment from 'moment'; + import pako from 'pako'; type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void; @@ -127,8 +128,28 @@ window.location.href = `${window.location.pathname}?gist=${gistURL}`; }; + const textEncode = (str) => { + if (window.TextEncoder) { + return new TextEncoder('utf-8').encode(str); + } + let utf8 = unescape(encodeURIComponent(str)); + let result = new Uint8Array(utf8.length); + for (let i = 0; i < utf8.length; i++) { + result[i] = utf8.charCodeAt(i); + } + return result; + }; + + const getKrokiCode = (source) => { + const data = textEncode(source); + const compressed = pako.deflate(data, { level: 9, to: 'string' }); + let result = btoa(compressed).replace(/\+/g, '-').replace(/\//g, '_'); + return result; + }; + let iUrl: string; let svgUrl: string; + let krokiUrl: string; let mdCode: string; let imagemodeselected = 'auto'; let userimagesize = 1080; @@ -143,8 +164,10 @@ stateCopy.mermaid = JSON.parse(stateCopy.mermaid); } const b64Code = toBase64(JSON.stringify(stateCopy), true); + const krokiCode = getKrokiCode(stateCopy.code); iUrl = `${rendererUrl}/img/${b64Code}`; svgUrl = `${rendererUrl}/svg/${b64Code}`; + krokiUrl = `${krokiRendererUrl}/mermaid/svg/${krokiCode}`; mdCode = `[![](${iUrl})](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`; }); @@ -168,6 +191,9 @@ +
PNG size diff --git a/src/lib/util/env.ts b/src/lib/util/env.ts index 679c80b5da..f5e22f3086 100644 --- a/src/lib/util/env.ts +++ b/src/lib/util/env.ts @@ -1 +1,2 @@ export const rendererUrl = import.meta.env.MERMAID_RENDERER_URL ?? 'https://mermaid.ink'; +export const krokiRendererUrl = import.meta.env.MERMAID_KROKI_RENDERER_URL ?? 'https://kroki.io'; diff --git a/yarn.lock b/yarn.lock index 8fd60035ba..ec6368237d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3424,6 +3424,11 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +pako@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" From 4472b1921c43a4e8411542552763f268f5fd2e89 Mon Sep 17 00:00:00 2001 From: Marek Skrobacki Date: Tue, 18 Jan 2022 08:39:41 +0000 Subject: [PATCH 2/4] kroki: generate URL / compressed diagram on-demand Converting the diagram to base64 and compression are the operations that might be CPU heavy, it is better to perform these only when actually needed. After this commit, the URL will be generated only after the button is pressed instead of after every change to the text. --- src/lib/components/actions.svelte | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte index 59d2f2b350..5f05714d14 100644 --- a/src/lib/components/actions.svelte +++ b/src/lib/components/actions.svelte @@ -140,6 +140,13 @@ return result; }; + const onKrokiClick = () => { + const stateCopy = JSON.parse(JSON.stringify($codeStore)); + const krokiCode = getKrokiCode(stateCopy.code); + const krokiUrl = `${krokiRendererUrl}/mermaid/svg/${krokiCode}`; + return window.open(krokiUrl, '_blank'); + }; + const getKrokiCode = (source) => { const data = textEncode(source); const compressed = pako.deflate(data, { level: 9, to: 'string' }); @@ -149,7 +156,6 @@ let iUrl: string; let svgUrl: string; - let krokiUrl: string; let mdCode: string; let imagemodeselected = 'auto'; let userimagesize = 1080; @@ -164,10 +170,8 @@ stateCopy.mermaid = JSON.parse(stateCopy.mermaid); } const b64Code = toBase64(JSON.stringify(stateCopy), true); - const krokiCode = getKrokiCode(stateCopy.code); iUrl = `${rendererUrl}/img/${b64Code}`; svgUrl = `${rendererUrl}/svg/${b64Code}`; - krokiUrl = `${krokiRendererUrl}/mermaid/svg/${krokiCode}`; mdCode = `[![](${iUrl})](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`; }); @@ -191,8 +195,8 @@ -
From 6ab1b296b8e279c49a588935487c61e4003d53f6 Mon Sep 17 00:00:00 2001 From: Marek Skrobacki Date: Tue, 18 Jan 2022 08:51:20 +0000 Subject: [PATCH 3/4] kroki: switch from native to js-base64 btoa --- src/lib/components/actions.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte index 5f05714d14..f5b0d475ab 100644 --- a/src/lib/components/actions.svelte +++ b/src/lib/components/actions.svelte @@ -4,7 +4,7 @@ import Card from '$lib/components/card/card.svelte'; import { rendererUrl, krokiRendererUrl } from '$lib/util/env'; import { base64State, codeStore } from '$lib/util/state'; - import { toBase64 } from 'js-base64'; + import { toBase64, btoa as jsbtoa } from 'js-base64'; import moment from 'moment'; import pako from 'pako'; @@ -150,7 +150,7 @@ const getKrokiCode = (source) => { const data = textEncode(source); const compressed = pako.deflate(data, { level: 9, to: 'string' }); - let result = btoa(compressed).replace(/\+/g, '-').replace(/\//g, '_'); + let result = jsbtoa(compressed).replace(/\+/g, '-').replace(/\//g, '_'); return result; }; From c59351c84f633a33a0dc6c6f68506089651888b4 Mon Sep 17 00:00:00 2001 From: Marek Skrobacki Date: Wed, 19 Jan 2022 08:25:24 +0000 Subject: [PATCH 4/4] kroki: don't make copy of the state --- src/lib/components/actions.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte index f5b0d475ab..b1b915eaaf 100644 --- a/src/lib/components/actions.svelte +++ b/src/lib/components/actions.svelte @@ -141,8 +141,7 @@ }; const onKrokiClick = () => { - const stateCopy = JSON.parse(JSON.stringify($codeStore)); - const krokiCode = getKrokiCode(stateCopy.code); + const krokiCode = getKrokiCode($codeStore.code); const krokiUrl = `${krokiRendererUrl}/mermaid/svg/${krokiCode}`; return window.open(krokiUrl, '_blank'); };