Compare commits

..
Author SHA1 Message Date
Matthieu MORELandGitHub 65dce606cb Update tailwind.config.cjs 2021-12-16 00:27:29 +01:00
23 changed files with 1318 additions and 703 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v2
- run: npx browserslist@latest --update-db
- name: Commit changes
uses: EndBug/add-and-commit@v8
uses: EndBug/add-and-commit@v7
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
+2 -1
View File
@@ -2,6 +2,7 @@ name: Update Monaco-editor
on:
workflow_dispatch:
pull_request:
types: [opened]
branches:
- 'dependabot/npm_and_yarn/develop/monaco-editor-**'
@@ -12,7 +13,7 @@ jobs:
- uses: actions/checkout@v2
- run: ./bin/update-monaco
- name: Commit changes
uses: EndBug/add-and-commit@v8
uses: EndBug/add-and-commit@v7
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
-4
View File
@@ -1,4 +0,0 @@
{
"editor.formatOnSave": true,
"cSpell.words": ["pako", "Serde", "serdes"]
}
+1 -1
View File
@@ -6,7 +6,7 @@
# Stop : press ctrl + c
# or
# docker stop mermaid-live-editor
FROM node:17.4.0 as mermaid-live-editor-builder
FROM node:17.2.0 as mermaid-live-editor-builder
COPY --chown=node:node . /home
WORKDIR /home
RUN yarn install
-5
View File
@@ -32,11 +32,6 @@ 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
-10
View File
@@ -30,14 +30,4 @@ describe('Auto sync tests', () => {
cy.get('#view').should('not.have.class', 'outOfSync');
cy.getLocalStorage('codeStore').snapshot();
});
it('supports commenting code out/in', () => {
const cmd = Cypress.platform === 'darwin' ? 'meta' : 'ctrl';
cy.get('#editor').type(`{uparrow}{${cmd}}/`);
cy.get('#view').contains('Car').should('not.exist');
cy.get('#editor').type(`{uparrow}{${cmd}}/`);
cy.get('#view').contains('Car').should('exist');
});
});
+14 -42
View File
@@ -60,21 +60,20 @@ describe('Site Loads', () => {
cy.getLocalStorage('codeStore').snapshot();
});
// Disabled temporarily. Should be enabled after the issue is fixed in Mermaid.
// it('should prevent setting the "securityLevel" option via URL', () => {
// const b64State = toBase64(
// `{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
// true
// );
// cy.on('window:confirm', () => true);
// cy.visit(`/edit#${b64State}`);
// cy.contains('Config').click();
// cy.contains('forest');
// cy.contains('securityLevel').should('not.exist');
// cy.get('#view').find('img').should('not.exist');
// cy.get('#view').contains('<img');
// cy.get('#view').contains(`src='https://via.placeholder.com/64'`);
// });
it('should prevent setting the "securityLevel" option via URL', () => {
const b64State = toBase64(
`{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
true
);
cy.on('window:confirm', () => true);
cy.visit(`/edit#${b64State}`);
cy.contains('Config').click();
cy.contains('forest');
cy.contains('securityLevel').should('not.exist');
cy.get('#view').find('img').should('not.exist');
cy.get('#view').contains('<img');
cy.get('#view').contains(`src='https://via.placeholder.com/64'`);
});
it('should allow persisting "securityLevel" using confirm dialogue', () => {
const b64State = toBase64(
@@ -93,31 +92,4 @@ describe('Site Loads', () => {
cy.visit('/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAg');
cy.contains('Please Click here to Raise an issue in github.');
});
it('should load uncompressed URL', () => {
cy.visit(
'/edit/#eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW05ldyBZZWFyXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfVxuICAgIEMgLS0-fE9uZXwgRFtMYXB0b3BdXG4gICAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXSIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9'
);
cy.contains('New Year');
cy.visit(
'/edit#eyJjb2RlIjoiY2xhc3NEaWFncmFtXG4gICAgQW5pbWFsIDx8LS0gRHVja1xuICAgIEFuaW1hbCA8fC0tIEZpc2hcbiAgICBBbmltYWwgPHwtLSBaZWJyYVxuICAgIEFuaW1hbCA6ICtpbnQgYWdlXG4gICAgQW5pbWFsIDogK1N0cmluZyBnZW5kZXJcbiAgICBBbmltYWw6ICtpc01hbW1hbCgpXG4gICAgQW5pbWFsOiArbWF0ZSgpXG4gICAgY2xhc3MgRHVja3tcbiAgICAgICtTdHJpbmcgYmVha0NvbG9yXG4gICAgICArc3dpbSgpXG4gICAgICArcXVhY2soKVxuICAgIH1cbiAgICBjbGFzcyBGaXNoe1xuICAgICAgLWludCBzaXplSW5GZWV0XG4gICAgICAtY2FuRWF0KClcbiAgICB9XG4gICAgY2xhc3MgWmVicmF7XG4gICAgICArYm9vbCBpc193aWxkXG4gICAgICArcnVuKClcbiAgICB9XG4gICAgICAgICAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGFya1wiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
);
cy.contains('Animal');
cy.visit(
'/edit/#base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW05ldyBZZWFyXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfVxuICAgIEMgLS0-fE9uZXwgRFtMYXB0b3BdXG4gICAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXSIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9'
);
cy.contains('New Year');
});
it('should load compressed URL', () => {
cy.visit(
'/edit#pako:eNpVkM2KwkAQhF-l6dMK5gVyEDRxvYi7sF6WjIcm0zqDzg_jBJEk725Hd2G3Tw31VVFUj23QjCWeEkUD-1p5kFs2O77BN1M6QFEshg1ncMHzfYDV2ybA1YQYrT_NXvhqgqDqtxPGkI315_ElVU__h-cB6mZLMYd4-Kvsb2GAdWM_jcT_V0xicb03RyqPVLSUoJI-OEfHyZHV0rqfDAqzYccKS3k1pbNC5Ufhuqgp81rbHBJKxuXKc6Quh6-7b7HMqeNfqLYkC7gfanwAlW1ZvQ'
);
cy.contains('New Year');
cy.visit(
'/edit#pako:eNptkU1PwzAMhv9K5BOI9Q9EXBDbJA477YYqITcxndV8QD40weh_Jy1rGR0-OY_tV2_sEyivCSQogzGuGduAtnaixINji0bcf1WVWGfVXdMtx8M1faYm4B8sxR27JLClJd6nwK4VLTlN4bI4jMQd2pLe3C4KFhNNcLQ92jv9ADGLNoTdozc-zIV4ZDsNlud7RtVN7_5Sb_jYrFcN3iN_0pPbEqUZK3QbTP_Ojyv4NdR4bwTHlyMbPcOQ3WJ2CliBpWCRdbnLqFJDOpClGmRJNYauhtr1pS-_6bKMjebkA8hXNJFWgDn5_YdTIFPINDWdb3vu6r8BaWOZRQ'
);
cy.reload();
cy.contains('Animal');
});
});
+1 -1
View File
@@ -16,7 +16,7 @@ module.exports = {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
}
},
"__version": "9.3.1",
"__version": "8.5.0",
"Auto sync tests": {
"should dim diagram when code is edited": {
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}"
+22 -25
View File
@@ -15,16 +15,15 @@
},
"devDependencies": {
"@cypress/snapshot": "^2.1.7",
"@sveltejs/adapter-static": "1.0.0-next.26",
"@sveltejs/kit": "1.0.0-next.260",
"@sveltejs/adapter-static": "1.0.0-next.22",
"@sveltejs/kit": "1.0.0-next.202",
"@types/mermaid": "^8.2.7",
"@types/pako": "^1.0.3",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"autoprefixer": "^10.4.2",
"chai": "^4.3.6",
"cssnano": "^5.0.16",
"cypress": "9.4.1",
"autoprefixer": "^10.4.0",
"chai": "^4.3.4",
"cssnano": "^5.0.13",
"cypress": "9.1.1",
"cypress-localstorage-commands": "^1.6.1",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.1.0",
@@ -32,34 +31,32 @@
"eslint-plugin-es": "^4.1.0",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-postcss-modules": "^2.0.0",
"eslint-plugin-svelte3": "^3.4.0",
"eslint-plugin-tailwindcss": "^3.4.3",
"eslint-plugin-svelte3": "^3.2.1",
"eslint-plugin-tailwindcss": "^1.17.2",
"husky": "^7.0.4",
"lint-staged": "^12.3.3",
"mocha": "^9.2.0",
"node-html-parser": "^5.2.0",
"postcss": "^8.4.6",
"postcss-load-config": "^3.1.1",
"lint-staged": "^12.1.2",
"mocha": "^9.1.3",
"node-html-parser": "^5.1.0",
"postcss": "^8.4.5",
"postcss-load-config": "^3.1.0",
"prettier": "~2.5.1",
"prettier-plugin-svelte": "^2.6.0",
"svelte": "^3.46.4",
"svelte-preprocess": "^4.10.2",
"tailwindcss": "^3.0.18",
"prettier-plugin-svelte": "^2.5.1",
"svelte": "^3.44.3",
"svelte-preprocess": "^4.10.0",
"tailwindcss": "^3.0.5",
"tslib": "^2.3.1",
"typescript": "^4.5.5"
"typescript": "^4.5.4"
},
"type": "module",
"dependencies": {
"@analytics/google-analytics": "^0.5.3",
"@macfja/svelte-persistent-store": "^1.2.0",
"analytics": "^0.7.21",
"daisyui": "1.25.4",
"analytics": "^0.7.19",
"daisyui": "1.16.5",
"js-base64": "^3.7.2",
"mermaid": "8.13.10",
"mermaid": "8.13.5",
"moment": "^2.29.1",
"monaco-editor": "^0.32.0",
"monaco-mermaid": "^1.0.3",
"pako": "2.0.4",
"monaco-editor": "^0.31.1",
"random-word-slugs": "^0.1.6"
},
"lint-staged": {
+7 -7
View File
@@ -11,31 +11,31 @@
<meta
name="description"
content="Simplify documentation and avoid heavy tools. Open source Visio Alternative. Commonly used for explaining your code! Mermaid is a simple markdown-like script language for generating charts from text via javascript." />
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="manifest" href="manifest.json" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.32.0/min/vs/editor/editor.main.min.css"
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/editor/editor.main.min.css"
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<script>
var require = {
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.32.0/min/vs' }
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs' }
};
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.32.0/min/vs/loader.min.js"
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/loader.min.js"
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.32.0/min/vs/editor/editor.main.nls.min.js"
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/editor/editor.main.nls.min.js"
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.32.0/min/vs/editor/editor.main.js"
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/editor/editor.main.js"
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
-9
View File
@@ -1,9 +0,0 @@
import type { Handle } from '@sveltejs/kit/types/hooks';
export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event, {
ssr: false
});
return response;
};
+13 -14
View File
@@ -2,9 +2,8 @@
import { browser } from '$app/env';
import Card from '$lib/components/card/card.svelte';
import { krokiRendererUrl, rendererUrl } from '$lib/util/env';
import { pakoSerde } from '$lib/util/serde';
import { serializedState, codeStore } from '$lib/util/state';
import { rendererUrl } from '$lib/util/env';
import { base64State, codeStore } from '$lib/util/state';
import { toBase64 } from 'js-base64';
import moment from 'moment';
@@ -18,7 +17,7 @@
}
const svgString = svg.outerHTML
.replaceAll('<br>', '<br/>')
.replaceAll(/<img([^>]*)>/g, (m, g: string) => `<img ${g} />`);
.replaceAll(/<img([^>]*)>/g, (m, g) => `<img ${g} />`);
return toBase64(svgString);
};
@@ -79,7 +78,7 @@
canvas.toBlob((blob) => {
try {
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
void navigator.clipboard.write([
navigator.clipboard.write([
/* eslint-disable no-undef */
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
new ClipboardItem({
@@ -109,7 +108,7 @@
};
const onCopyMarkdown = () => {
(document.getElementById('markdown') as HTMLInputElement).select();
document.getElementById('markdown').select();
document.execCommand('Copy');
};
@@ -130,7 +129,6 @@
let iUrl: string;
let svgUrl: string;
let krokiUrl: string;
let mdCode: string;
let imagemodeselected = 'auto';
let userimagesize = 1080;
@@ -139,10 +137,14 @@
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
isNetlify = true;
}
serializedState.subscribe((encodedState: string) => {
iUrl = `${rendererUrl}/img/${encodedState}`;
svgUrl = `${rendererUrl}/svg/${encodedState}`;
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize($codeStore.code)}`;
base64State.subscribe((encodedState: string) => {
const stateCopy = JSON.parse(JSON.stringify($codeStore));
if (typeof stateCopy.mermaid === 'string') {
stateCopy.mermaid = JSON.parse(stateCopy.mermaid);
}
const b64Code = toBase64(JSON.stringify(stateCopy), true);
iUrl = `${rendererUrl}/img/${b64Code}`;
svgUrl = `${rendererUrl}/svg/${b64Code}`;
mdCode = `[![](${iUrl})](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`;
});
</script>
@@ -166,9 +168,6 @@
<button class="action-btn flex-auto">
<a target="_blank" href={svgUrl}><i class="fas fa-external-link-alt mr-2" /> SVG</a>
</button>
<button class="action-btn flex-auto">
<a target="_blank" href={krokiUrl}><i class="fas fa-external-link-alt mr-2" /> Kroki</a>
</button>
<div class="flex gap-2 items-center">
PNG size
@@ -4,7 +4,7 @@
import { themeStore } from '$lib/util/theme';
import type monaco from 'monaco-editor';
import { createEventDispatcher, onMount } from 'svelte';
import initEditor from 'monaco-mermaid';
import { initEditor } from './util';
let divEl: HTMLDivElement = null;
let editor: monaco.editor.IStandaloneCodeEditor;
+552
View File
@@ -0,0 +1,552 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const initEditor = (monacoEditor): void => {
monacoEditor.languages.register({ id: 'mermaid' });
const requirementDiagrams = [
'requirement',
'functionalRequirement',
'interfaceRequirement',
'performanceRequirement',
'physicalRequirement',
'designConstraint'
];
const keywords: {
[key: string]: {
typeKeywords: string[];
blockKeywords: string[];
keywords: string[];
};
} = {
flowchart: {
typeKeywords: ['flowchart', 'flowchart-v2', 'graph'],
blockKeywords: ['subgraph', 'end'],
keywords: [
'TB',
'TD',
'BT',
'RL',
'LR',
'click',
'call',
'href',
'_self',
'_blank',
'_parent',
'_top',
'linkStyle',
'style',
'classDef',
'class',
'direction',
'interpolate'
]
},
sequenceDiagram: {
typeKeywords: ['sequenceDiagram'],
blockKeywords: ['alt', 'par', 'and', 'loop', 'else', 'end', 'rect', 'opt', 'alt', 'rect'],
keywords: [
'participant',
'as',
'Note',
'note',
'right of',
'left of',
'over',
'activate',
'deactivate',
'autonumber',
'title',
'actor'
]
},
classDiagram: {
typeKeywords: ['classDiagram', 'classDiagram-v2'],
blockKeywords: ['class'],
keywords: [
'link',
'click',
'callback',
'call',
'href',
'cssClass',
'direction',
'TB',
'BT',
'RL',
'LR'
]
},
stateDiagram: {
typeKeywords: ['stateDiagram', 'stateDiagram-v2'],
blockKeywords: ['state', 'note', 'end'],
keywords: ['state', 'as', 'hide empty description', 'direction', 'TB', 'BT', 'RL', 'LR']
},
erDiagram: {
typeKeywords: ['erDiagram'],
blockKeywords: [],
keywords: []
},
journey: {
typeKeywords: ['journey'],
blockKeywords: ['section'],
keywords: ['title']
},
info: {
typeKeywords: ['info'],
blockKeywords: [],
keywords: ['showInfo']
},
gantt: {
typeKeywords: ['gantt'],
blockKeywords: [],
keywords: [
'title',
'dateFormat',
'axisFormat',
'todayMarker',
'section',
'excludes',
'inclusiveEndDates'
]
},
requirementDiagram: {
typeKeywords: ['requirement', 'requirementDiagram'],
blockKeywords: requirementDiagrams.concat('element'),
keywords: []
},
gitGraph: {
typeKeywords: ['gitGraph'],
blockKeywords: [],
keywords: ['commit', 'branch', 'merge', 'reset', 'checkout', 'LR', 'BT']
},
pie: {
typeKeywords: ['pie'],
blockKeywords: [],
keywords: ['title', 'showData']
}
};
// Register a tokens provider for the mermaid language
monacoEditor.languages.setMonarchTokensProvider('mermaid', {
...Object.entries(keywords)
.map((entry) =>
Object.fromEntries(
Object.entries(entry[1]).map((deepEntry) => [
entry[0] + deepEntry[0][0].toUpperCase() + deepEntry[0].slice(1),
deepEntry[1]
])
)
)
.reduce((overallKeywords, nextKeyword) => ({ ...overallKeywords, ...nextKeyword }), {}),
tokenizer: {
root: [
[/%%(?=.*%%$)/, { token: 'string', nextEmbedded: 'json' }],
[/%%$/, { token: 'string', nextEmbedded: '@pop' }],
[/^\s*gitGraph/m, 'typeKeyword', 'gitGraph'],
[/^\s*info/m, 'typeKeyword', 'info'],
[/^\s*pie/m, 'typeKeyword', 'pie'],
[/^\s*(flowchart|flowchart-v2|graph)/m, 'typeKeyword', 'flowchart'],
[/^\s*sequenceDiagram/, 'typeKeyword', 'sequenceDiagram'],
[/^\s*classDiagram(-v2)?/, 'typeKeyword', 'classDiagram'],
[/^\s*journey/, 'typeKeyword', 'journey'],
[/^\s*gantt/, 'typeKeyword', 'gantt'],
[/^\s*stateDiagram(-v2)?/, 'typeKeyword', 'stateDiagram'],
[/^\s*erDiagram/, 'typeKeyword', 'erDiagram'],
[/^\s*requirement(Diagram)?/, 'typeKeyword', 'requirementDiagram'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
gitGraph: [
[/option(?=s)/, { token: 'typeKeyword', next: 'optionsGitGraph' }],
[/(^\s*branch|reset|merge|checkout)(.*$)/, ['keyword', 'variable']],
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@gitGraphBlockKeywords': 'typeKeyword',
'@gitGraphKeywords': 'keyword'
}
}
],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/".*?"/, 'string'],
[/\^/, 'delimiter.bracket']
],
optionsGitGraph: [
[/s$/, { token: 'typeKeyword', nextEmbedded: 'json', matchOnlyAtLineStart: false }],
['end', { token: 'typeKeyword', next: '@pop', nextEmbedded: '@pop' }]
],
info: [
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@infoBlockKeywords': 'typeKeyword',
'@infoKeywords': 'keyword'
}
}
]
],
pie: [
[/(title)(.*$)/, ['keyword', 'string']],
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@pieBlockKeywords': 'typeKeyword',
'@pieKeywords': 'keyword'
}
}
],
[/".*?"/, 'string'],
[/\s*\d+/, 'number'],
[/:/, 'delimiter.bracket'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
flowchart: [
[/[ox]?(--+|==+)[ox]/, 'transition'],
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@flowchartBlockKeywords': 'typeKeyword',
'@flowchartKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/\|+.+?\|+/, 'string'],
[/\[+(\\.+?[\\/]|\/.+?[/\\])\]+/, 'string'],
[/[[>]+[^\]|[]+?\]+/, 'string'],
[/{+.+?}+/, 'string'],
[/\(+.+?\)+/, 'string'],
[/-\.+->?/, 'transition'],
[/(-[-.])([^->]+?)(-{3,}|-{2,}>|\.-+>)/, ['transition', 'string', 'transition']],
[/(==+)([^=]+?)(={3,}|={2,}>)/, ['transition', 'string', 'transition']],
[/<?(--+|==+)>|===+|---+/, 'transition'],
[/:::/, 'transition'],
[/[;&]/, 'delimiter.bracket'],
[/".*?"/, 'string'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
sequenceDiagram: [
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@sequenceDiagramBlockKeywords': 'typeKeyword',
'@sequenceDiagramKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/(--?>?>|--?[)x])[+-]?/, 'transition'],
[/(:)([^:\n]*?$)/, ['delimiter.bracket', 'string']],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
classDiagram: [
[/(?!class\s)([a-zA-Z]+)(\s+[a-zA-Z]+)/, ['type', 'variable']],
[/(\*|<\|?|o)?(--|\.\.)(\*|\|?>|o)?/, 'transition'],
[/^\s*class\s(?!.*\{)/, 'keyword'],
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@classDiagramBlockKeywords': 'typeKeyword',
'@classDiagramKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/(<<)(.+?)(>>)/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']],
[/".*?"/, 'string'],
[/:::/, 'transition'],
[/:|\+|-|#|~|\*\s*$|\$\s*$|\(|\)|{|}/, 'delimiter.bracket']
],
journey: [
[/(title)(.*)/, ['keyword', 'string']],
[/(section)(.*)/, ['typeKeyword', 'string']],
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@journeyBlockKeywords': 'typeKeyword',
'@journeyKeywords': 'keyword',
'@default': 'variable'
}
}
],
[
/(^\s*.+?)(:)(.*?)(:)(.*?)([,$])/,
[
'string',
'delimiter.bracket',
'number',
'delimiter.bracket',
'variable',
'delimiter.bracket'
]
],
[/,/, 'delimiter.bracket'],
[/(^\s*.+?)(:)([^:]*?)$/, ['string', 'delimiter.bracket', 'variable']],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
gantt: [
[/(title)(.*)/, ['keyword', 'string']],
[/(section)(.*)/, ['typeKeyword', 'string']],
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@ganttBlockKeywords': 'typeKeyword',
'@ganttKeywords': 'keyword'
}
}
],
[/(^\s*.*?)(:)/, ['string', 'delimiter.bracket']],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/:/, 'delimiter.bracket']
],
stateDiagram: [
[/note[^:]*$/, { token: 'typeKeyword', next: 'stateDiagramNote' }],
['hide empty description', 'keyword'],
[/^\s*state\s(?!.*\{)/, 'keyword'],
[/(<<)(fork|join|choice)(>>)/, 'annotation'],
[/(\[\[)(fork|join|choice)(]])/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']],
[
/[a-zA-Z][\w$]*/,
{
cases: {
'@stateDiagramBlockKeywords': 'typeKeyword',
'@stateDiagramKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/".*?"/, 'string'],
[/(:)([^:\n]*?$)/, ['delimiter.bracket', 'string']],
[/{|}/, 'delimiter.bracket'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/-->/, 'transition'],
[/\[.*?]/, 'string']
],
stateDiagramNote: [
[/^\s*end note$/, { token: 'typeKeyword', next: '@pop' }],
[/.*/, 'string']
],
erDiagram: [
[/[}|][o|](--|\.\.)[o|][{|]/, 'transition'],
[/".*?"/, 'string'],
[/(:)(.*?$)/, ['delimiter.bracket', 'string']],
[/:|{|}/, 'delimiter.bracket'],
[/([a-zA-Z]+)(\s+[a-zA-Z]+)/, ['type', 'variable']],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/[a-zA-Z_-][\w$]*/, 'variable']
],
requirementDiagram: [
[/->|<-|-/, 'transition'],
[/(\d+\.)*\d+/, 'number'],
[
/[a-zA-Z_-][\w$]*/,
{
cases: {
'@requirementDiagramBlockKeywords': 'typeKeyword',
'@default': 'variable'
}
}
],
[/:|{|}|\//, 'delimiter.bracket'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/".*?"/, 'string']
]
}
});
monacoEditor.editor.defineTheme('mermaid-dark', {
base: 'vs-dark',
inherit: true,
rules: [
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
{ token: 'transition', foreground: '008800', fontStyle: 'bold' }
],
colors: {}
});
monacoEditor.editor.defineTheme('mermaid', {
base: 'vs',
inherit: true,
rules: [
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
{ token: 'keyword', foreground: '649696' },
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
{ token: 'string', foreground: 'AA8500' },
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
{ token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' },
{ token: 'annotation', foreground: '4b4b96' },
{ token: 'number', foreground: '4b4b96' },
{ token: 'comment', foreground: '888c89' },
{ token: 'variable', foreground: 'A22889' },
{ token: 'type', foreground: '2BDEA8' }
],
colors: {}
});
// Register a completion item provider for the mermaid language
monacoEditor.languages.registerCompletionItemProvider('mermaid', {
provideCompletionItems: () => {
const suggestions = [
{
label: 'loop',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['loop ${1:Loop text}', '\t$0', 'end'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Sequence Diagram Loops'
},
{
label: 'alt',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['alt ${1:Describing text}', '\t$0', 'else', '\t', 'end'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Alternative Path'
},
{
label: 'opt',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['opt ${1:Describing text}', '\t$0', 'end'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Optional Path'
},
{
label: 'par',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: [
'par ${1:[Action 1]}',
'\t$0',
'and ${2:[Action 2]}',
'\t',
'and ${3:[Action 3]}',
'\t',
'end'
].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Parallel Actions'
},
{
label: 'rect',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['rect ${1:rgb(0, 255, 0)}', '\t$0', 'end'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Background Color'
},
{
label: 'subgraph',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['subgraph ${1:title}', '\t$0', 'end'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Subgraph'
},
{
label: 'class',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['class ${1:className} {', '\t$0', '}'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Class'
},
{
label: 'state',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['state ${1:stateName} {', '\t$0', '}'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'State'
},
{
label: 'note',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['note ${1:right of State1}', '\t$0', 'end note'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'State'
},
{
label: 'section',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['section ${1:Go to work}', '\t$0'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'User-journey Section'
},
{
label: 'element',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['element ${1:test_entity} {', '\t$0', '}'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Requirement Diagram Element'
},
{
label: 'options',
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: ['options', '{', ' $0', '}', 'end'].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'Git Graph Options'
},
...requirementDiagrams.map((requirementDiagramType) => ({
label: requirementDiagramType,
kind: monacoEditor.languages.CompletionItemKind.Snippet,
insertText: [
requirementDiagramType + ' ${1:test_req} {',
'\tid: 1',
'\ttext: the test text.',
'\trisk: high',
'\tverifyMethod: test',
'}'
].join('\n'),
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: requirementDiagramType
.split(/(?=[A-Z])/)
.map((part) => part[0].toUpperCase() + part.slice(1))
.join(' ')
})),
...[
...new Set(
Object.values(keywords)
.map((diagramKeywords) =>
Object.entries(diagramKeywords)
.filter((keywordType) => keywordType[0] !== 'annotations')
.map((entry) => entry[1])
)
.flat(2)
)
].map((keyword) => ({
label: keyword,
kind: monacoEditor.languages.CompletionItemKind.Keyword,
insertText: keyword
}))
];
return { suggestions: suggestions };
}
});
monacoEditor.languages.setLanguageConfiguration('mermaid', {
autoClosingPairs: [
{
open: '(',
close: ')'
},
{
open: '{',
close: '}'
},
{
open: '[',
close: ']'
}
],
brackets: [
['(', ')'],
['{', '}'],
['[', ']']
]
});
};
+18
View File
@@ -69,6 +69,24 @@
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
`,
'Git Graph': `gitGraph:
options
{
"nodeSpacing": 150,
"nodeRadius": 10
}
end
commit
branch newbranch
checkout newbranch
commit
commit
checkout master
commit
commit
merge newbranch
`,
'User Journey': ` journey
title My working day
+1 -4
View File
@@ -1,4 +1 @@
export const rendererUrl: string =
(import.meta.env.MERMAID_RENDERER_URL as string) ?? 'https://mermaid.ink';
export const krokiRendererUrl: string =
(import.meta.env.MERMAID_KROKI_RENDERER_URL as string) ?? 'https://kroki.io';
export const rendererUrl = import.meta.env.MERMAID_RENDERER_URL ?? 'https://mermaid.ink';
-61
View File
@@ -1,61 +0,0 @@
import { deflate, inflate } from 'pako';
import { toUint8Array, fromUint8Array, toBase64, fromBase64 } from 'js-base64';
import type { State } from '$lib/types';
interface Serde {
serialize: (state: string) => string;
deserialize: (state: string) => string;
}
const base64Serde: Serde = {
serialize: (state: string): string => {
return toBase64(state, true);
},
deserialize: (state: string): string => {
return fromBase64(state);
}
};
export const pakoSerde: Serde = {
serialize: (state: string): string => {
const data = new TextEncoder().encode(state);
const compressed = deflate(data, { level: 9 });
return fromUint8Array(compressed, true);
},
deserialize: (state: string): string => {
const data = toUint8Array(state);
return inflate(data, { to: 'string' });
}
};
const serdes: { [key: string]: Serde } = {
base64: base64Serde,
pako: pakoSerde
};
type SerdeType = keyof typeof serdes;
export const serializeState = (state: State): string => {
const json = JSON.stringify(state);
const defaultSerde: SerdeType = 'pako';
const serialized = serdes[defaultSerde].serialize(json);
return `${defaultSerde}:${serialized}`;
};
export const deserializeState = (state: string): State => {
let type: SerdeType, serialized: string;
if (state.includes(':')) {
let tempType: string;
[tempType, serialized] = state.split(':');
if (tempType in serdes) {
type = tempType;
} else {
throw new Error(`Unknown serde type: ${tempType}`);
}
} else {
type = 'base64';
serialized = state;
}
const json = serdes[type].deserialize(serialized);
return JSON.parse(json) as State;
};
+8 -7
View File
@@ -1,9 +1,9 @@
import { writable, get, derived } from 'svelte/store';
import type { Readable } from 'svelte/store';
import { toBase64, fromBase64 } from 'js-base64';
import { persist, localStorage } from '@macfja/svelte-persistent-store';
import type { State } from '$lib/types';
import { saveStatistics } from './stats';
import { serializeState, deserializeState } from './serde';
export const defaultState: State = {
code: `graph TD
@@ -37,16 +37,17 @@ const urlParseFailedState = `graph TD
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
export const serializedState: Readable<string> = derived([codeStore], ([code], set) => {
set(serializeState(code));
export const base64State: Readable<string> = derived([codeStore], ([code], set) => {
set(toBase64(JSON.stringify(code), true));
});
export const loadState = (data: string): void => {
let state: State;
console.log('Loading', data);
try {
state = deserializeState(data);
const mermaidConfig: { [key: string]: string } =
const stateStr = fromBase64(data);
console.log(`Trying to load state: ${stateStr}`);
state = JSON.parse(stateStr);
const mermaidConfig =
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
if (
mermaidConfig.securityLevel &&
@@ -115,7 +116,7 @@ export const toggleDarkTheme = (dark: boolean): void => {
};
export const initURLSubscription = (): void => {
serializedState.subscribe((state: string) => {
base64State.subscribe((state: string) => {
history.replaceState(undefined, undefined, `#${state}`);
});
};
+5 -5
View File
@@ -1,12 +1,12 @@
<script lang="ts">
import Editor from '$lib/components/editor.svelte';
import Editor from '$lib/components/editor/editor.svelte';
import Navbar from '$lib/components/navbar.svelte';
import Preset from '$lib/components/preset.svelte';
import Actions from '$lib/components/actions.svelte';
import View from '$lib/components/view.svelte';
import Card from '$lib/components/card/card.svelte';
import History from '$lib/components/history/history.svelte';
import { updateCode, updateConfig, codeStore, serializedState } from '$lib/util/state';
import { updateCode, updateConfig, codeStore, base64State } from '$lib/util/state';
import { initHandler, syncDiagram } from '$lib/util/util';
import { errorStore } from '$lib/util/error';
import { onMount } from 'svelte';
@@ -15,7 +15,7 @@
import type { EditorUpdateEvent, State, Tab } from '$lib/types';
import { base } from '$app/paths';
serializedState; // Weird fix for error > serializedState is not defined. Treeshaking?
base64State; // Weird fix for error > base64State is not defined. Treeshaking?
let selectedMode = 'code';
const languageMap = {
code: 'mermaid',
@@ -82,7 +82,7 @@
startLineNumber: e.hash.loc.first_line,
startColumn: e.hash.loc.first_column,
endLineNumber: e.hash.loc.last_line,
endColumn: (e.hash.loc.last_column as number) + 1,
endColumn: e.hash.loc.last_column + 1,
message: e.str
};
errorMarkers.push(marker);
@@ -96,7 +96,7 @@
};
const viewDiagram = () => {
window.open(`${base}/view#${$serializedState}`, '_blank').focus();
window.open(`${base}/view#${$base64State}`, '_blank').focus();
};
onMount(async () => {
+3
View File
@@ -19,6 +19,9 @@ const config = {
base: `/mermaid-live-editor${process.env['BETA'] ? '/beta' : ''}`
}
: {},
ssr: false,
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte',
trailingSlash: 'ignore',
vite: {
envPrefix: 'MERMAID_',
+1
View File
@@ -1,4 +1,5 @@
module.exports = {
mode: 'jit',
plugins: [require('daisyui')],
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
+2 -3
View File
@@ -2,7 +2,7 @@
"compilerOptions": {
"moduleResolution": "node",
"module": "es2020",
"lib": ["es2020", "ESNext", "DOM.Iterable", "DOM"],
"lib": ["es2020", "ESNext", "DOM.Iterable"],
"target": "es2019",
/**
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
@@ -33,7 +33,6 @@
"src/**/*.svelte",
"cypress/**/*.ts",
"cypress/**/*.js",
"static/**/*.js",
"src/pako.mts"
"static/**/*.js"
]
}
+666 -502
View File
File diff suppressed because it is too large Load Diff