diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index f4f6f285..7be5a785 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,50 +1,50 @@
module.exports = {
- root: true,
- parser: '@typescript-eslint/parser',
- extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- // 'plugin:@typescript-eslint/recommended-requiring-type-checking',
- 'prettier'
- ],
- plugins: ['svelte3', 'tailwindcss', '@typescript-eslint', 'es', 'vitest'],
- ignorePatterns: [
- 'docs/*',
- '*.cjs',
- '*.js',
- '*.md',
- 'snapshots.js',
- 'svelte.config.js',
- 'renovate.json',
- 'package.json',
- 'tsconfig.json'
- ],
- overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
- settings: {
- 'svelte3/typescript': () => require('typescript')
- },
- parserOptions: {
- sourceType: 'module',
- ecmaVersion: 2020,
- tsconfigRootDir: __dirname,
- project: ['./tsconfig.json'],
- extraFileExtensions: ['.svelte'],
- allowAutomaticSingleRunInference: true
- },
- env: {
- browser: true,
- es2020: true
- },
- rules: {
- '@typescript-eslint/ban-ts-comment': [
- 'error',
- {
- 'ts-ignore': 'allow-with-description'
- }
- ],
- '@typescript-eslint/no-unsafe-member-access': 'off',
- '@typescript-eslint/no-unsafe-assignment': 'off',
- 'es/no-regexp-lookbehind-assertions': 'error',
- curly: ['error', 'all']
- }
+ root: true,
+ parser: '@typescript-eslint/parser',
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ // 'plugin:@typescript-eslint/recommended-requiring-type-checking',
+ 'prettier'
+ ],
+ plugins: ['svelte3', 'tailwindcss', '@typescript-eslint', 'es', 'vitest'],
+ ignorePatterns: [
+ 'docs/*',
+ '*.cjs',
+ '*.js',
+ '*.md',
+ 'snapshots.js',
+ 'svelte.config.js',
+ 'renovate.json',
+ 'package.json',
+ 'tsconfig.json'
+ ],
+ overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
+ settings: {
+ 'svelte3/typescript': () => require('typescript')
+ },
+ parserOptions: {
+ sourceType: 'module',
+ ecmaVersion: 2020,
+ tsconfigRootDir: __dirname,
+ project: ['./tsconfig.json'],
+ extraFileExtensions: ['.svelte'],
+ allowAutomaticSingleRunInference: true
+ },
+ env: {
+ browser: true,
+ es2020: true
+ },
+ rules: {
+ '@typescript-eslint/ban-ts-comment': [
+ 'error',
+ {
+ 'ts-ignore': 'allow-with-description'
+ }
+ ],
+ '@typescript-eslint/no-unsafe-member-access': 'off',
+ '@typescript-eslint/no-unsafe-assignment': 'off',
+ 'es/no-regexp-lookbehind-assertions': 'error',
+ curly: ['error', 'all']
+ }
};
diff --git a/.prettierrc b/.prettierrc
index 53609cfb..ab65daf2 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,8 +1,7 @@
{
- "singleQuote": true,
- "svelteSortOrder": "options-scripts-markup-styles",
- "bracketSameLine": true,
- "useTabs": true,
- "trailingComma": "none",
- "printWidth": 100
+ "singleQuote": true,
+ "svelteSortOrder": "options-scripts-markup-styles",
+ "bracketSameLine": true,
+ "trailingComma": "none",
+ "printWidth": 100
}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 197e0480..1689524c 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,15 +1,15 @@
{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "type": "pwa-chrome",
- "request": "launch",
- "name": "Launch Chrome against localhost",
- "url": "http://localhost:3000",
- "webRoot": "${workspaceFolder}"
- }
- ]
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "pwa-chrome",
+ "request": "launch",
+ "name": "Launch Chrome against localhost",
+ "url": "http://localhost:3000",
+ "webRoot": "${workspaceFolder}"
+ }
+ ]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 47967284..a7a60240 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,9 +1,9 @@
{
- "editor.formatOnSave": true,
- "cSpell.words": ["asyncable", "pako", "Serde", "serdes"],
- "vitest.commandLine": "yarn test:unit",
- "vitest.enable": true,
- "testing.autoRun.mode": "rerun",
- "svelte.enable-ts-plugin": true,
- "githubPullRequests.ignoredPullRequestBranches": ["develop"]
+ "editor.formatOnSave": true,
+ "cSpell.words": ["asyncable", "pako", "Serde", "serdes"],
+ "vitest.commandLine": "yarn test:unit",
+ "vitest.enable": true,
+ "testing.autoRun.mode": "rerun",
+ "svelte.enable-ts-plugin": true,
+ "githubPullRequests.ignoredPullRequestBranches": ["develop"]
}
diff --git a/bin/update-monaco.js b/bin/update-monaco.js
index 3d81bdfc..875639ba 100755
--- a/bin/update-monaco.js
+++ b/bin/update-monaco.js
@@ -9,37 +9,37 @@ const monacoVersion = packageJson.dependencies['monaco-editor'].replace('^', '')
// fetch monaco sri info from cdnjs api
const cdnjsAPIResp = await fetch(
- `https://api.cdnjs.com/libraries/monaco-editor/${monacoVersion}?fields=sri`
+ `https://api.cdnjs.com/libraries/monaco-editor/${monacoVersion}?fields=sri`
);
if (cdnjsAPIResp.ok) {
- const respJson = await cdnjsAPIResp.json();
- const htmlPath = path.join('src', 'app.html');
- const appHtml = fs
- .readFileSync(htmlPath, 'utf8')
- // update monaco version of every asset in app.html
- .replaceAll(/[0-9.]+\/min\/vs/g, `${monacoVersion}/min/vs`);
- const root = parse(appHtml);
- const updateIntegrity = (tag, attr) => {
- for (const node of root
- .getElementsByTagName(tag)
- .filter((node) => node.getAttribute(attr)?.includes('monaco-editor'))) {
- const file = node.getAttribute(attr).split(`${monacoVersion}/`)[1];
- node.setAttribute('integrity', respJson.sri[file]);
- }
- };
+ const respJson = await cdnjsAPIResp.json();
+ const htmlPath = path.join('src', 'app.html');
+ const appHtml = fs
+ .readFileSync(htmlPath, 'utf8')
+ // update monaco version of every asset in app.html
+ .replaceAll(/[0-9.]+\/min\/vs/g, `${monacoVersion}/min/vs`);
+ const root = parse(appHtml);
+ const updateIntegrity = (tag, attr) => {
+ for (const node of root
+ .getElementsByTagName(tag)
+ .filter((node) => node.getAttribute(attr)?.includes('monaco-editor'))) {
+ const file = node.getAttribute(attr).split(`${monacoVersion}/`)[1];
+ node.setAttribute('integrity', respJson.sri[file]);
+ }
+ };
- updateIntegrity('script', 'src');
- updateIntegrity('link', 'href');
+ updateIntegrity('script', 'src');
+ updateIntegrity('link', 'href');
- fs.writeFileSync(
- htmlPath,
- prettier.format(root.toString(), {
- singleQuote: false,
- parser: 'html',
- bracketSameLine: true,
- useTabs: true
- })
- );
+ fs.writeFileSync(
+ htmlPath,
+ prettier.format(root.toString(), {
+ singleQuote: false,
+ parser: 'html',
+ bracketSameLine: true,
+ useTabs: true
+ })
+ );
} else {
- throw Error('Unable to fetch monaco sri data from cdnjs api.');
+ throw Error('Unable to fetch monaco sri data from cdnjs api.');
}
diff --git a/cypress.config.js b/cypress.config.js
index 32775249..0e2c5933 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -2,34 +2,34 @@ import { defineConfig } from 'cypress';
import fs from 'fs';
import { isFileExist, findFiles } from 'cy-verify-downloads';
export default defineConfig({
- projectId: '2ckppp',
- viewportWidth: 1440,
- viewportHeight: 768,
- snapshotFileName: './cypress/snapshots.js',
- defaultCommandTimeout: 16000,
- requestTimeout: 16000,
- retries: {
- runMode: 4,
- openMode: 0
- },
- e2e: {
- setupNodeEvents(on, config) {
- on('task', {
- isFileExist,
- findFiles,
- deleteFile(path) {
- fs.rmSync(path);
- return null;
- },
- readFileMaybe(filename) {
- if (fs.existsSync(filename)) {
- return fs.readFileSync(filename, 'utf8');
- }
- return null;
- }
- });
- },
- baseUrl: 'http://localhost:3000',
- specPattern: 'cypress/e2e/**/*.spec.ts'
- }
+ projectId: '2ckppp',
+ viewportWidth: 1440,
+ viewportHeight: 768,
+ snapshotFileName: './cypress/snapshots.js',
+ defaultCommandTimeout: 16000,
+ requestTimeout: 16000,
+ retries: {
+ runMode: 4,
+ openMode: 0
+ },
+ e2e: {
+ setupNodeEvents(on, config) {
+ on('task', {
+ isFileExist,
+ findFiles,
+ deleteFile(path) {
+ fs.rmSync(path);
+ return null;
+ },
+ readFileMaybe(filename) {
+ if (fs.existsSync(filename)) {
+ return fs.readFileSync(filename, 'utf8');
+ }
+ return null;
+ }
+ });
+ },
+ baseUrl: 'http://localhost:3000',
+ specPattern: 'cypress/e2e/**/*.spec.ts'
+ }
});
diff --git a/cypress/.eslintrc b/cypress/.eslintrc
index 43c00855..01edfeeb 100644
--- a/cypress/.eslintrc
+++ b/cypress/.eslintrc
@@ -1,10 +1,10 @@
{
- "plugins": ["cypress"],
- "extends": ["plugin:cypress/recommended"],
- "rules": {
- "jest/expect-expect": "off"
- },
- "env": {
- "cypress/globals": true
- }
+ "plugins": ["cypress"],
+ "extends": ["plugin:cypress/recommended"],
+ "rules": {
+ "jest/expect-expect": "off"
+ },
+ "env": {
+ "cypress/globals": true
+ }
}
diff --git a/cypress/e2e/actions.spec.ts b/cypress/e2e/actions.spec.ts
index b54c5f24..d004e81a 100644
--- a/cypress/e2e/actions.spec.ts
+++ b/cypress/e2e/actions.spec.ts
@@ -1,48 +1,48 @@
import { verifyFileSize } from './util';
describe('Check actions', () => {
- beforeEach(() => {
- cy.clearLocalStorage();
- cy.visit('/edit');
- });
+ beforeEach(() => {
+ cy.clearLocalStorage();
+ cy.visit('/edit');
+ });
- it('should update markdown code', () => {
- cy.get('#markdown')
- .invoke('val')
- .then((oldText) => {
- cy.get('#editor').click('bottom').type('{enter}C --> HistoryTest');
- cy.get('#markdown')
- .invoke('val')
- .then((newText) => {
- expect(oldText).to.not.eq(newText);
- });
- });
- });
+ it('should update markdown code', () => {
+ cy.get('#markdown')
+ .invoke('val')
+ .then((oldText) => {
+ cy.get('#editor').click('bottom').type('{enter}C --> HistoryTest');
+ cy.get('#markdown')
+ .invoke('val')
+ .then((newText) => {
+ expect(oldText).to.not.eq(newText);
+ });
+ });
+ });
- it('should load gists from URL', () => {
- cy.get('#gist').type('https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a');
- cy.contains('Load Gist').click();
- cy.contains('Go shopping!!');
- });
+ it('should load gists from URL', () => {
+ cy.get('#gist').type('https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a');
+ cy.contains('Load Gist').click();
+ cy.contains('Go shopping!!');
+ });
- it('should download png and svg', () => {
- cy.clock(new Date(2022, 0, 1).getTime());
+ it('should download png and svg', () => {
+ cy.clock(new Date(2022, 0, 1).getTime());
- cy.get(`#downloadPNG`).click();
- verifyFileSize('diagram', 'png', 21_000);
+ cy.get(`#downloadPNG`).click();
+ verifyFileSize('diagram', 'png', 21_000);
- cy.get(`#downloadSVG`).click();
- verifyFileSize('diagram', 'svg', 10_000);
+ cy.get(`#downloadSVG`).click();
+ verifyFileSize('diagram', 'svg', 10_000);
- // Verify downloaded file is different for different diagrams
- cy.contains('Sample Diagrams').click();
- cy.contains('ER').click();
+ // Verify downloaded file is different for different diagrams
+ cy.contains('Sample Diagrams').click();
+ cy.contains('ER').click();
- cy.get(`#downloadPNG`).click();
- verifyFileSize('diagram', 'png', 46_000);
+ cy.get(`#downloadPNG`).click();
+ verifyFileSize('diagram', 'png', 46_000);
- cy.get(`#downloadSVG`).click();
- verifyFileSize('diagram', 'svg', 12_000);
+ cy.get(`#downloadSVG`).click();
+ verifyFileSize('diagram', 'svg', 12_000);
- cy.clock().invoke('restore');
- });
+ cy.clock().invoke('restore');
+ });
});
diff --git a/cypress/e2e/diagramUpdate.spec.ts b/cypress/e2e/diagramUpdate.spec.ts
index dd09a0a1..07bd430a 100644
--- a/cypress/e2e/diagramUpdate.spec.ts
+++ b/cypress/e2e/diagramUpdate.spec.ts
@@ -1,86 +1,86 @@
import { getEditor, cmd } from './util';
describe('Auto sync tests', () => {
- beforeEach(() => {
- cy.clearLocalStorage();
- cy.visit('/');
- });
+ beforeEach(() => {
+ cy.clearLocalStorage();
+ cy.visit('/');
+ });
- it('should dim diagram when code is edited', () => {
- cy.contains('Auto sync').click();
- cy.get('#view').should('not.have.class', 'outOfSync');
- cy.get('#errorContainer').should('not.exist');
- getEditor({ bottom: true, newline: true }).type(' C --> Test');
- cy.get('#view').should('have.class', 'outOfSync');
- cy.get('#errorContainer').should('contain.text', 'Diagram out of sync.');
- cy.getLocalStorage('codeStore').snapshot();
- });
+ it('should dim diagram when code is edited', () => {
+ cy.contains('Auto sync').click();
+ cy.get('#view').should('not.have.class', 'outOfSync');
+ cy.get('#errorContainer').should('not.exist');
+ getEditor({ bottom: true, newline: true }).type(' C --> Test');
+ cy.get('#view').should('have.class', 'outOfSync');
+ cy.get('#errorContainer').should('contain.text', 'Diagram out of sync.');
+ cy.getLocalStorage('codeStore').snapshot();
+ });
- it('should update diagram when shortcut is used', () => {
- cy.contains('Auto sync').click();
- cy.get('#view').should('not.have.class', 'outOfSync');
- getEditor().type(' C --> Test');
- cy.get('#view').should('have.class', 'outOfSync');
- cy.get('#errorContainer').should('contain.text', 'Diagram out of sync.');
- getEditor().type(`${cmd}{enter}`);
- cy.get('#errorContainer').should('not.exist');
- cy.get('#view').should('not.have.class', 'outOfSync');
- });
+ it('should update diagram when shortcut is used', () => {
+ cy.contains('Auto sync').click();
+ cy.get('#view').should('not.have.class', 'outOfSync');
+ getEditor().type(' C --> Test');
+ cy.get('#view').should('have.class', 'outOfSync');
+ cy.get('#errorContainer').should('contain.text', 'Diagram out of sync.');
+ getEditor().type(`${cmd}{enter}`);
+ cy.get('#errorContainer').should('not.exist');
+ cy.get('#view').should('not.have.class', 'outOfSync');
+ });
- it('should show/hide sync button with auto sync', () => {
- cy.get('[data-cy=sync]').should('not.exist');
- cy.contains('Auto sync').click();
- cy.get('[data-cy=sync]').should('exist');
- cy.get('#autoSync').check();
- cy.get('[data-cy=sync]').should('not.exist');
- });
+ it('should show/hide sync button with auto sync', () => {
+ cy.get('[data-cy=sync]').should('not.exist');
+ cy.contains('Auto sync').click();
+ cy.get('[data-cy=sync]').should('exist');
+ cy.get('#autoSync').check();
+ cy.get('[data-cy=sync]').should('not.exist');
+ });
- it('should not dim diagram when code is in sync', () => {
- cy.contains('Auto sync').click();
- cy.get('#view').should('not.have.class', 'outOfSync');
- getEditor().type(' C --> Test');
- cy.get('#view').should('have.class', 'outOfSync');
- cy.get('[data-cy=sync]').click();
- cy.get('#view').should('not.have.class', 'outOfSync');
- cy.get('#autoSync').check();
- getEditor().type('ing');
- cy.get('#view').should('not.have.class', 'outOfSync');
- cy.getLocalStorage('codeStore').snapshot();
- });
+ it('should not dim diagram when code is in sync', () => {
+ cy.contains('Auto sync').click();
+ cy.get('#view').should('not.have.class', 'outOfSync');
+ getEditor().type(' C --> Test');
+ cy.get('#view').should('have.class', 'outOfSync');
+ cy.get('[data-cy=sync]').click();
+ cy.get('#view').should('not.have.class', 'outOfSync');
+ cy.get('#autoSync').check();
+ getEditor().type('ing');
+ cy.get('#view').should('not.have.class', 'outOfSync');
+ cy.getLocalStorage('codeStore').snapshot();
+ });
- it('supports commenting code out/in', () => {
- getEditor().type(`{uparrow}${cmd}/`);
- cy.get('#view').contains('Car').should('not.exist');
+ it('supports commenting code out/in', () => {
+ getEditor().type(`{uparrow}${cmd}/`);
+ cy.get('#view').contains('Car').should('not.exist');
- getEditor().type(`{uparrow}${cmd}/`);
- cy.get('#view').contains('Car').should('exist');
- });
+ getEditor().type(`{uparrow}${cmd}/`);
+ cy.get('#view').contains('Car').should('exist');
+ });
- it('supports editing code when code is incorrect', () => {
- cy.visit(
- '/edit#pako:eNpljjEKwzAMRa8SNOcEnlt6gK5eVFvYJsgOqkwpIXevg9smEE1PnyfxF3DFExgISW-CczQ2D21cYU7a-SGYXRwyvTp9jUhuKlVP-eHy7zA-leQsMEmg_QOM0BLG5FujZVMsaCQmC6ahR5ks2Lw2r84ela4-aREwKpVGwKrl_s7ut3fnkjAIcg_XDzuaUhs'
- );
- cy.get('#errorContainer').should('not.exist');
- getEditor({ newline: true }).type(`branch test`);
- cy.get('#editor').contains('branch test').should('exist');
- cy.get('#errorContainer')
- .contains(
- 'Error: Trying to checkout branch which is not yet created. (Help try using "branch master")'
- )
- .should('exist');
- });
+ it('supports editing code when code is incorrect', () => {
+ cy.visit(
+ '/edit#pako:eNpljjEKwzAMRa8SNOcEnlt6gK5eVFvYJsgOqkwpIXevg9smEE1PnyfxF3DFExgISW-CczQ2D21cYU7a-SGYXRwyvTp9jUhuKlVP-eHy7zA-leQsMEmg_QOM0BLG5FujZVMsaCQmC6ahR5ks2Lw2r84ela4-aREwKpVGwKrl_s7ut3fnkjAIcg_XDzuaUhs'
+ );
+ cy.get('#errorContainer').should('not.exist');
+ getEditor({ newline: true }).type(`branch test`);
+ cy.get('#editor').contains('branch test').should('exist');
+ cy.get('#errorContainer')
+ .contains(
+ 'Error: Trying to checkout branch which is not yet created. (Help try using "branch master")'
+ )
+ .should('exist');
+ });
});
describe('Pan and Zoom', () => {
- beforeEach(() => {
- cy.clearLocalStorage();
- cy.visit('/');
- });
- it('should toggle pan and zoom', () => {
- cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
- cy.contains('Pan & Zoom').click();
- cy.get('#svg-pan-zoom-reset-pan-zoom').should('exist');
- cy.contains('Pan & Zoom').click();
- cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
- });
+ beforeEach(() => {
+ cy.clearLocalStorage();
+ cy.visit('/');
+ });
+ it('should toggle pan and zoom', () => {
+ cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
+ cy.contains('Pan & Zoom').click();
+ cy.get('#svg-pan-zoom-reset-pan-zoom').should('exist');
+ cy.contains('Pan & Zoom').click();
+ cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
+ });
});
diff --git a/cypress/e2e/docMap.spec.ts b/cypress/e2e/docMap.spec.ts
index 0d0dfc0d..5c7e8201 100755
--- a/cypress/e2e/docMap.spec.ts
+++ b/cypress/e2e/docMap.spec.ts
@@ -1,36 +1,36 @@
describe('Editor docs tests', () => {
- beforeEach(() => {
- cy.on('uncaught:exception', () => {
- return false;
- });
- cy.clearLocalStorage();
- cy.visit('/edit');
- cy.contains('Sample Diagrams').click();
- });
+ beforeEach(() => {
+ cy.on('uncaught:exception', () => {
+ return false;
+ });
+ cy.clearLocalStorage();
+ cy.visit('/edit');
+ cy.contains('Sample Diagrams').click();
+ });
- it('Test default loading', () => {
- cy.get(`[data-cy=docs][href^="https://mermaid-js.github.io/mermaid"]`).should('exist');
- });
+ it('Test default loading', () => {
+ cy.get(`[data-cy=docs][href^="https://mermaid-js.github.io/mermaid"]`).should('exist');
+ });
- it('Test to see if the correct URL loads when changing from one diagram to other', () => {
- cy.contains('Flow').click();
- cy.get(`[data-cy=docs][href$="/#/flowchart"]`).should('exist');
+ it('Test to see if the correct URL loads when changing from one diagram to other', () => {
+ cy.contains('Flow').click();
+ cy.get(`[data-cy=docs][href$="/#/flowchart"]`).should('exist');
- cy.contains('Config').click();
- cy.get(`[data-cy=docs][href$="/#/flowchart?id=configuration"]`).should('exist');
+ cy.contains('Config').click();
+ cy.get(`[data-cy=docs][href$="/#/flowchart?id=configuration"]`).should('exist');
- cy.contains('Sequence').click();
- cy.get(`[data-cy=docs][href$="/#/sequenceDiagram?id=configuration"]`).should('exist');
+ cy.contains('Sequence').click();
+ cy.get(`[data-cy=docs][href$="/#/sequenceDiagram?id=configuration"]`).should('exist');
- cy.contains('Code').click();
- cy.get(`[data-cy=docs][href$="/#/sequenceDiagram"]`).should('exist');
- });
+ cy.contains('Code').click();
+ cy.get(`[data-cy=docs][href$="/#/sequenceDiagram"]`).should('exist');
+ });
- it("Test to check URLs for a case where config URL doesn't exist", () => {
- cy.contains('State').click();
- cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
+ it("Test to check URLs for a case where config URL doesn't exist", () => {
+ cy.contains('State').click();
+ cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
- cy.contains('Config').click();
- cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
- });
+ cy.contains('Config').click();
+ cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
+ });
});
diff --git a/cypress/e2e/history.spec.ts b/cypress/e2e/history.spec.ts
index e9e32fda..af5305c9 100644
--- a/cypress/e2e/history.spec.ts
+++ b/cypress/e2e/history.spec.ts
@@ -1,105 +1,105 @@
import { getEditor, verifyFileSnapshot } from './util';
describe('Save History', () => {
- beforeEach(() => {
- cy.clock(new Date(2022, 0, 1).getTime());
- cy.clearLocalStorage();
- cy.visit('/edit');
+ beforeEach(() => {
+ cy.clock(new Date(2022, 0, 1).getTime());
+ cy.clearLocalStorage();
+ cy.visit('/edit');
- cy.contains('Actions').click();
- cy.contains('History').click();
- });
+ cy.contains('Actions').click();
+ cy.contains('History').click();
+ });
- afterEach(() => {
- cy.clock().invoke('restore');
- });
+ afterEach(() => {
+ cy.clock().invoke('restore');
+ });
- it('should load history from localstorage', () => {
- cy.setLocalStorage(
- 'manualHistoryStore',
- '[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","id":"d7ea820e-21dd-418a-b984-fd58acde09df","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"b749ffc6-522b-4a44-86cf-7c1ffc3146b3","name":"helpful-ocean"}]'
- );
- cy.setLocalStorage(
- 'autoHistoryStore',
- '[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","id":"69ea820e-522b-4a44-86cf-fd58acde09df","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"x749ffc6-21dd-418a-b984-7c1ffc3146b3","name":"needy-mosquito"}]'
- );
- cy.reload();
- cy.contains('Actions').click();
- cy.contains('History').click();
- cy.get('#historyList').find('li').should('have.length', 2);
- cy.get('#historyList').find('No items in History').should('not.exist');
- cy.get('#historyList').contains('helpful-ocean');
- cy.get('#historyList').contains('hollow-art');
- cy.contains('Restore').click();
- cy.contains('Halloween');
- cy.contains('Timeline').click();
+ it('should load history from localstorage', () => {
+ cy.setLocalStorage(
+ 'manualHistoryStore',
+ '[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","id":"d7ea820e-21dd-418a-b984-fd58acde09df","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"b749ffc6-522b-4a44-86cf-7c1ffc3146b3","name":"helpful-ocean"}]'
+ );
+ cy.setLocalStorage(
+ 'autoHistoryStore',
+ '[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","id":"69ea820e-522b-4a44-86cf-fd58acde09df","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"x749ffc6-21dd-418a-b984-7c1ffc3146b3","name":"needy-mosquito"}]'
+ );
+ cy.reload();
+ cy.contains('Actions').click();
+ cy.contains('History').click();
+ cy.get('#historyList').find('li').should('have.length', 2);
+ cy.get('#historyList').find('No items in History').should('not.exist');
+ cy.get('#historyList').contains('helpful-ocean');
+ cy.get('#historyList').contains('hollow-art');
+ cy.contains('Restore').click();
+ cy.contains('Halloween');
+ cy.contains('Timeline').click();
- cy.get('#historyList').find('li').should('have.length', 2);
- cy.get('#historyList').find('No items in History').should('not.exist');
- cy.get('#historyList').contains('needy-mosquito');
- cy.get('#historyList').contains('barking-dog');
- cy.contains('Restore').click();
- cy.contains('New Year');
- });
+ cy.get('#historyList').find('li').should('have.length', 2);
+ cy.get('#historyList').find('No items in History').should('not.exist');
+ cy.get('#historyList').contains('needy-mosquito');
+ cy.get('#historyList').contains('barking-dog');
+ cy.contains('Restore').click();
+ cy.contains('New Year');
+ });
- it('should save when clicked', () => {
- cy.get('#historyList').find('li').should('have.length', 0);
- cy.get('#historyList').contains('No items in History');
- cy.get('#saveHistory').click();
- cy.get('#historyList').find('No items in History').should('not.exist');
- cy.get('#historyList').find('li').should('have.length', 1);
- cy.get('#saveHistory').click();
- cy.on('window:alert', (str) => {
- expect(str).to.equal('State already saved.');
- });
- cy.on('window:confirm', () => true);
- getEditor().type(' C --> HistoryTest');
- cy.get('#saveHistory').click();
- cy.get('#historyList').find('li').should('have.length', 2);
- });
+ it('should save when clicked', () => {
+ cy.get('#historyList').find('li').should('have.length', 0);
+ cy.get('#historyList').contains('No items in History');
+ cy.get('#saveHistory').click();
+ cy.get('#historyList').find('No items in History').should('not.exist');
+ cy.get('#historyList').find('li').should('have.length', 1);
+ cy.get('#saveHistory').click();
+ cy.on('window:alert', (str) => {
+ expect(str).to.equal('State already saved.');
+ });
+ cy.on('window:confirm', () => true);
+ getEditor().type(' C --> HistoryTest');
+ cy.get('#saveHistory').click();
+ cy.get('#historyList').find('li').should('have.length', 2);
+ });
- it('should be able to restore and delete', () => {
- cy.get('#saveHistory').click();
- getEditor().type(' C --> HistoryTest');
- cy.get('#historyList').find('No items in History').should('not.exist');
- cy.get('#historyList').find('li').should('have.length', 1);
- cy.contains('HistoryTest');
- cy.contains('Restore').click();
- cy.contains('HistoryTest').should('not.exist');
- cy.contains('Delete').click();
- cy.get('#historyList').find('li').should('have.length', 0);
- cy.get('#historyList').contains('No items in History');
- cy.get('#saveHistory').click();
- getEditor().type(' C --> HistoryTest');
- cy.get('#saveHistory').click();
- cy.get('#editor').type('ing');
- cy.get('#clearHistory').click();
- cy.on('window:alert', (str) => {
- expect(str).to.equal('Clear all saved items?');
- });
- cy.on('window:confirm', () => true);
- cy.get('#historyList').contains('No items in History');
- });
+ it('should be able to restore and delete', () => {
+ cy.get('#saveHistory').click();
+ getEditor().type(' C --> HistoryTest');
+ cy.get('#historyList').find('No items in History').should('not.exist');
+ cy.get('#historyList').find('li').should('have.length', 1);
+ cy.contains('HistoryTest');
+ cy.contains('Restore').click();
+ cy.contains('HistoryTest').should('not.exist');
+ cy.contains('Delete').click();
+ cy.get('#historyList').find('li').should('have.length', 0);
+ cy.get('#historyList').contains('No items in History');
+ cy.get('#saveHistory').click();
+ getEditor().type(' C --> HistoryTest');
+ cy.get('#saveHistory').click();
+ cy.get('#editor').type('ing');
+ cy.get('#clearHistory').click();
+ cy.on('window:alert', (str) => {
+ expect(str).to.equal('Clear all saved items?');
+ });
+ cy.on('window:confirm', () => true);
+ cy.get('#historyList').contains('No items in History');
+ });
- // TODO: Fix #639
- xit('should auto save history', () => {
- getEditor().type(' C --> HistoryTest');
- cy.tick(70000);
- cy.contains('Timeline').click();
- cy.get('#historyList').find('li').should('have.length', 1);
- cy.get('#editor').type('ing');
- cy.tick(70000);
- cy.get('#historyList').find('li').should('have.length', 2);
- for (let i = 0; i < 31; i++) {
- cy.get('#editor').type('.');
- cy.tick(70000);
- }
- cy.get('#historyList').find('li').should('have.length', 30);
- });
+ // TODO: Fix #639
+ xit('should auto save history', () => {
+ getEditor().type(' C --> HistoryTest');
+ cy.tick(70000);
+ cy.contains('Timeline').click();
+ cy.get('#historyList').find('li').should('have.length', 1);
+ cy.get('#editor').type('ing');
+ cy.tick(70000);
+ cy.get('#historyList').find('li').should('have.length', 2);
+ for (let i = 0; i < 31; i++) {
+ cy.get('#editor').type('.');
+ cy.tick(70000);
+ }
+ cy.get('#historyList').find('li').should('have.length', 30);
+ });
- it('should download history', () => {
- cy.get('#saveHistory').click();
- cy.get(`#downloadHistory`).click();
- verifyFileSnapshot('history', 'json', 'A[Christmas] -->|Get money| B(Go shopping)');
- });
+ it('should download history', () => {
+ cy.get('#saveHistory').click();
+ cy.get(`#downloadHistory`).click();
+ verifyFileSnapshot('history', 'json', 'A[Christmas] -->|Get money| B(Go shopping)');
+ });
});
diff --git a/cypress/e2e/loadSite.spec.ts b/cypress/e2e/loadSite.spec.ts
index f39b2957..b80104e2 100644
--- a/cypress/e2e/loadSite.spec.ts
+++ b/cypress/e2e/loadSite.spec.ts
@@ -1,122 +1,122 @@
import { toBase64 } from 'js-base64';
describe('Site Loads', () => {
- beforeEach(() => {
- cy.clearLocalStorage();
- cy.visit('/');
- });
+ beforeEach(() => {
+ cy.clearLocalStorage();
+ cy.visit('/');
+ });
- it('Check Home page load', () => {
- cy.url().should('include', '/edit');
- cy.contains('History').click();
- cy.getLocalStorage('codeStore').snapshot();
- });
+ it('Check Home page load', () => {
+ cy.url().should('include', '/edit');
+ cy.contains('History').click();
+ cy.getLocalStorage('codeStore').snapshot();
+ });
- it('Check Redirect from old URL', () => {
- cy.visit(
- '/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
- );
- cy.url().should('include', '/edit#pako:eNp');
- });
+ it('Check Redirect from old URL', () => {
+ cy.visit(
+ '/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
+ );
+ cy.url().should('include', '/edit#pako:eNp');
+ });
- it('should load sample diagrams when clicked', () => {
- cy.contains('Sample Diagrams').click();
- cy.contains('Pie').click();
- cy.contains('pie title Pets adopted by volunteers');
- cy.contains('Class').click();
- cy.contains('classDiagram');
- });
+ it('should load sample diagrams when clicked', () => {
+ cy.contains('Sample Diagrams').click();
+ cy.contains('Pie').click();
+ cy.contains('pie title Pets adopted by volunteers');
+ cy.contains('Class').click();
+ cy.contains('classDiagram');
+ });
- (Cypress.env('CI') === 'true' ? describe : describe.skip)('github', () => {
- it('should load diagram from gist', () => {
- cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`);
- cy.contains('History').click();
- cy.contains('Go shopping!!');
- cy.contains('Revisions');
- cy.contains('sidharthv96 v8f8f1e2');
- cy.contains('sidharthv96 v7851e19');
- cy.getLocalStorage('codeStore').snapshot();
- });
+ (Cypress.env('CI') === 'true' ? describe : describe.skip)('github', () => {
+ it('should load diagram from gist', () => {
+ cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`);
+ cy.contains('History').click();
+ cy.contains('Go shopping!!');
+ cy.contains('Revisions');
+ cy.contains('sidharthv96 v8f8f1e2');
+ cy.contains('sidharthv96 v7851e19');
+ cy.getLocalStorage('codeStore').snapshot();
+ });
- it('should load diagram from gist revision', () => {
- cy.visit(
- '/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19'
- );
- cy.contains('History').click();
- cy.contains('Party');
- cy.contains('Revisions');
- cy.contains('sidharthv96 v7851e19');
- cy.getLocalStorage('codeStore').snapshot();
- });
+ it('should load diagram from gist revision', () => {
+ cy.visit(
+ '/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19'
+ );
+ cy.contains('History').click();
+ cy.contains('Party');
+ cy.contains('Revisions');
+ cy.contains('sidharthv96 v7851e19');
+ cy.getLocalStorage('codeStore').snapshot();
+ });
- it('should load diagram from raw files', () => {
- cy.visit(
- '/edit?code=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd&config=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json'
- );
- cy.contains('Party');
- cy.getLocalStorage('codeStore').snapshot();
- });
- });
+ it('should load diagram from raw files', () => {
+ cy.visit(
+ '/edit?code=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd&config=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json'
+ );
+ cy.contains('Party');
+ 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[\\"
\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","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('
{
+ // const b64State = toBase64(
+ // `{"code":"graph TD\\nA[\\"
\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","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('
{
- const b64State = toBase64(
- `{"code":"graph TD\\nA[\\"
\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
- true
- );
- cy.on('window:confirm', () => false);
- cy.visit(`/edit#${b64State}`);
- cy.get('#editor').type(' ');
- cy.contains('Config').click();
- cy.contains('forest');
- cy.contains('securityLevel');
- cy.get('#view').find('img').should('be.visible');
- });
+ it.skip('should allow persisting "securityLevel" using confirm dialogue', () => {
+ const b64State = toBase64(
+ `{"code":"graph TD\\nA[\\"
\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
+ true
+ );
+ cy.on('window:confirm', () => false);
+ cy.visit(`/edit#${b64State}`);
+ cy.get('#editor').type(' ');
+ cy.contains('Config').click();
+ cy.contains('forest');
+ cy.contains('securityLevel');
+ cy.get('#view').find('img').should('be.visible');
+ });
- it('should show troubleshooting steps if loading fails', () => {
- cy.visit('/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAg');
- cy.reload(true);
- cy.contains('Please Click here to Raise an issue in github.');
- });
+ it('should show troubleshooting steps if loading fails', () => {
+ cy.visit('/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAg');
+ cy.reload(true);
+ 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 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.contains('Animal');
- });
+ 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.contains('Animal');
+ });
});
diff --git a/cypress/e2e/themes.spec.ts b/cypress/e2e/themes.spec.ts
index a5a5c77f..c7d986ff 100644
--- a/cypress/e2e/themes.spec.ts
+++ b/cypress/e2e/themes.spec.ts
@@ -1,56 +1,56 @@
describe('Test themes', () => {
- describe('Test light themes', () => {
- beforeEach(() => {
- cy.clearLocalStorage();
- cy.visit('/edit', {
- onBeforeLoad(win) {
- cy.stub(win, 'matchMedia')
- .callThrough()
- .withArgs('(prefers-color-scheme: dark)')
- .returns({
- matches: false
- });
- }
- });
- cy.contains('Change Theme').click();
- });
+ describe('Test light themes', () => {
+ beforeEach(() => {
+ cy.clearLocalStorage();
+ cy.visit('/edit', {
+ onBeforeLoad(win) {
+ cy.stub(win, 'matchMedia')
+ .callThrough()
+ .withArgs('(prefers-color-scheme: dark)')
+ .returns({
+ matches: false
+ });
+ }
+ });
+ cy.contains('Change Theme').click();
+ });
- it('should set light theme as default', () => {
- cy.contains('light').parent().should('have.class', 'bordered');
- cy.contains('dark').parent().should('not.have.class', 'bordered');
- cy.getLocalStorage('themeStore').snapshot();
- });
+ it('should set light theme as default', () => {
+ cy.contains('light').parent().should('have.class', 'bordered');
+ cy.contains('dark').parent().should('not.have.class', 'bordered');
+ cy.getLocalStorage('themeStore').snapshot();
+ });
- it('should change themes when clicked', () => {
- cy.contains('light').parent().should('have.class', 'bordered');
- cy.contains('cupcake').click();
- cy.contains('cupcake').parent().should('have.class', 'bordered');
- cy.contains('light').parent().should('not.have.class', 'bordered');
- cy.contains('dark').parent().should('not.have.class', 'bordered');
- cy.getLocalStorage('themeStore').snapshot();
- });
- });
+ it('should change themes when clicked', () => {
+ cy.contains('light').parent().should('have.class', 'bordered');
+ cy.contains('cupcake').click();
+ cy.contains('cupcake').parent().should('have.class', 'bordered');
+ cy.contains('light').parent().should('not.have.class', 'bordered');
+ cy.contains('dark').parent().should('not.have.class', 'bordered');
+ cy.getLocalStorage('themeStore').snapshot();
+ });
+ });
- describe('Test dark mode', () => {
- beforeEach(() => {
- cy.clearLocalStorage();
- cy.visit('/edit', {
- onBeforeLoad(win) {
- cy.stub(win, 'matchMedia')
- .callThrough()
- .withArgs('(prefers-color-scheme: dark)')
- .returns({
- matches: true
- });
- }
- });
- cy.contains('Change Theme').click();
- });
+ describe('Test dark mode', () => {
+ beforeEach(() => {
+ cy.clearLocalStorage();
+ cy.visit('/edit', {
+ onBeforeLoad(win) {
+ cy.stub(win, 'matchMedia')
+ .callThrough()
+ .withArgs('(prefers-color-scheme: dark)')
+ .returns({
+ matches: true
+ });
+ }
+ });
+ cy.contains('Change Theme').click();
+ });
- it('should set dark theme as default', () => {
- cy.contains('light').parent().should('not.have.class', 'bordered');
- cy.contains('dark').parent().should('have.class', 'bordered');
- cy.getLocalStorage('themeStore').snapshot();
- });
- });
+ it('should set dark theme as default', () => {
+ cy.contains('light').parent().should('not.have.class', 'bordered');
+ cy.contains('dark').parent().should('have.class', 'bordered');
+ cy.getLocalStorage('themeStore').snapshot();
+ });
+ });
});
diff --git a/cypress/e2e/util.ts b/cypress/e2e/util.ts
index cf0303ba..73fe6dd2 100644
--- a/cypress/e2e/util.ts
+++ b/cypress/e2e/util.ts
@@ -1,41 +1,41 @@
export const cmd = `{${Cypress.platform === 'darwin' ? 'meta' : 'ctrl'}}`;
export const getEditor = ({ bottom = true, newline = false } = {}) =>
- cy
- .get('#editor textarea:first')
- .click()
- .focused()
- .type(`${bottom ? '{pageDown}' : cmd}`)
- .type(`${newline ? '{enter}' : cmd}`);
+ cy
+ .get('#editor textarea:first')
+ .click()
+ .focused()
+ .type(`${bottom ? '{pageDown}' : cmd}`)
+ .type(`${newline ? '{enter}' : cmd}`);
const downloadsFolder = Cypress.config('downloadsFolder');
export const verifyFileSize = (
- fileType: 'history' | 'diagram',
- extension: string,
- size: number
+ fileType: 'history' | 'diagram',
+ extension: string,
+ size: number
) => {
- const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
- const filePath = `${downloadsFolder}/${fileName}`;
- cy.verifyDownload(fileName);
- cy.readFile(filePath, null, {
- log: false
- }).then((buffer) => expect((buffer as ArrayBuffer).byteLength).to.be.gt(size));
- cy.task('deleteFile', filePath);
+ const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
+ const filePath = `${downloadsFolder}/${fileName}`;
+ cy.verifyDownload(fileName);
+ cy.readFile(filePath, null, {
+ log: false
+ }).then((buffer) => expect((buffer as ArrayBuffer).byteLength).to.be.gt(size));
+ cy.task('deleteFile', filePath);
};
export const verifyFileSnapshot = (
- fileType: 'history' | 'diagram',
- extension: string,
- content: string
+ fileType: 'history' | 'diagram',
+ extension: string,
+ content: string
) => {
- const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
- const filePath = `${downloadsFolder}/${fileName}`;
- cy.verifyDownload(fileName);
- cy.readFile(filePath, null, {
- log: false
- }).then((buffer) =>
- expect(new TextDecoder('utf-8').decode(buffer as ArrayBuffer)).to.contain(content)
- );
- cy.task('deleteFile', filePath);
+ const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
+ const filePath = `${downloadsFolder}/${fileName}`;
+ cy.verifyDownload(fileName);
+ cy.readFile(filePath, null, {
+ log: false
+ }).then((buffer) =>
+ expect(new TextDecoder('utf-8').decode(buffer as ArrayBuffer)).to.contain(content)
+ );
+ cy.task('deleteFile', filePath);
};
diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json
index 0ff2d645..02e42543 100644
--- a/cypress/fixtures/example.json
+++ b/cypress/fixtures/example.json
@@ -1,5 +1,5 @@
{
- "name": "Using fixtures to represent data",
- "email": "hello@cypress.io",
- "body": "Fixtures are a great way to mock data for responses to routes"
+ "name": "Using fixtures to represent data",
+ "email": "hello@cypress.io",
+ "body": "Fixtures are a great way to mock data for responses to routes"
}
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index fe4e2603..0f4147cd 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -27,12 +27,12 @@ import { register } from '@cypress/snapshot';
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
register();
declare global {
- // eslint-disable-next-line @typescript-eslint/no-namespace
- namespace Cypress {
- interface Chainable {
- snapshot(): void;
- }
- }
+ // eslint-disable-next-line @typescript-eslint/no-namespace
+ namespace Cypress {
+ interface Chainable {
+ snapshot(): void;
+ }
+ }
}
import 'cypress-localstorage-commands';
diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js
index af0278d8..d9401131 100644
--- a/cypress/support/e2e.js
+++ b/cypress/support/e2e.js
@@ -21,8 +21,8 @@ require('cy-verify-downloads').addCustomCommand();
// require('./commands')
Cypress.on('uncaught:exception', (err) => {
- /* returning false here prevents Cypress from failing the test */
- if (err.message.includes('ResizeObserver loop limit exceeded')) {
- return false;
- }
+ /* returning false here prevents Cypress from failing the test */
+ if (err.message.includes('ResizeObserver loop limit exceeded')) {
+ return false;
+ }
});
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
index efeb6a0f..ec647435 100644
--- a/cypress/tsconfig.json
+++ b/cypress/tsconfig.json
@@ -1,7 +1,7 @@
{
- "compilerOptions": {
- "allowJs": true,
- "types": ["cypress", "cypress-localstorage-commands", "cy-verify-downloads"]
- },
- "include": ["**/*.ts"]
+ "compilerOptions": {
+ "allowJs": true,
+ "types": ["cypress", "cypress-localstorage-commands", "cy-verify-downloads"]
+ },
+ "include": ["**/*.ts"]
}
diff --git a/package.json b/package.json
index 4b7b9c1a..0975b5da 100644
--- a/package.json
+++ b/package.json
@@ -1,94 +1,94 @@
{
- "name": "mermaid-live-editor",
- "version": "2.0.67",
- "type": "module",
- "license": "MIT",
- "scripts": {
- "dev": "vite dev",
- "dev:force": "MERMAID_LOCAL=true yarn dev --force",
- "dev:test": "yarn dev",
- "build": "vite build",
- "preview": "vite preview",
- "lint": "prettier --check --cache --plugin-search-dir=. .;eslint --ignore-path .gitignore .",
- "lint:fix": "prettier --write --cache --plugin-search-dir=. .;eslint --fix --ignore-path .gitignore .",
- "format": "prettier --write --cache --plugin-search-dir=. .",
- "pre-commit": "lint-staged",
- "postinstall": "husky install; svelte-kit sync",
- "test:unit": "vitest",
- "test:ui": "vitest --ui",
- "test:coverage": "vitest run --coverage",
- "test:browser": "cypress run",
- "test": "test:unit && test:browser",
- "cy": "cypress open"
- },
- "devDependencies": {
- "@cypress/snapshot": "2.1.7",
- "@sveltejs/adapter-static": "1.0.0-next.46",
- "@sveltejs/kit": "1.0.0-next.522",
- "@testing-library/jest-dom": "5.16.5",
- "@testing-library/svelte": "3.2.2",
- "@types/pako": "2.0.0",
- "@types/uuid": "8.3.4",
- "@typescript-eslint/eslint-plugin": "5.42.0",
- "@typescript-eslint/parser": "5.42.0",
- "@vitest/ui": "0.24.3",
- "autoprefixer": "10.4.12",
- "c8": "7.12.0",
- "chai": "4.3.6",
- "cssnano": "5.1.13",
- "cy-verify-downloads": "0.1.11",
- "cypress": "10.11.0",
- "cypress-localstorage-commands": "2.2.1",
- "eslint": "8.26.0",
- "eslint-config-prettier": "8.5.0",
- "eslint-plugin-cypress": "2.12.1",
- "eslint-plugin-es": "4.1.0",
- "eslint-plugin-postcss-modules": "2.0.0",
- "eslint-plugin-svelte3": "4.0.0",
- "eslint-plugin-tailwindcss": "3.6.2",
- "eslint-plugin-vitest": "0.0.11",
- "esserializer": "1.3.2",
- "husky": "8.0.1",
- "jsdom": "20.0.1",
- "lint-staged": "13.0.3",
- "node-html-parser": "6.1.1",
- "postcss": "8.4.18",
- "postcss-load-config": "4.0.1",
- "prettier": "2.7.1",
- "prettier-plugin-svelte": "2.8.0",
- "svelte": "3.52.0",
- "svelte-preprocess": "4.10.7",
- "tailwindcss": "3.2.1",
- "tslib": "2.4.0",
- "typescript": "4.8.4",
- "vite": "3.2.2",
- "vitest": "0.24.3"
- },
- "dependencies": {
- "analytics": "0.8.1",
- "analytics-plugin-plausible": "0.0.6",
- "daisyui": "2.37.0",
- "js-base64": "3.7.2",
- "mermaid": "9.2.0",
- "moment": "2.29.4",
- "monaco-editor": "0.34.1",
- "monaco-mermaid": "1.0.6",
- "pako": "2.0.4",
- "random-word-slugs": "0.1.6",
- "svg-pan-zoom": "3.6.1",
- "uuid": "9.0.0"
- },
- "lint-staged": {
- "*.{ts,svelte,js,css,md,json}": [
- "prettier --plugin-search-dir=. --write",
- "eslint --ignore-path .gitignore "
- ]
- },
- "volta": {
- "node": "18.12.0",
- "yarn": "1.22.19"
- },
- "engines": {
- "node": ">=16.7"
- }
+ "name": "mermaid-live-editor",
+ "version": "2.0.67",
+ "type": "module",
+ "license": "MIT",
+ "scripts": {
+ "dev": "vite dev",
+ "dev:force": "MERMAID_LOCAL=true yarn dev --force",
+ "dev:test": "yarn dev",
+ "build": "vite build",
+ "preview": "vite preview",
+ "lint": "prettier --check --cache --plugin-search-dir=. .;eslint --ignore-path .gitignore .",
+ "lint:fix": "prettier --write --cache --plugin-search-dir=. .;eslint --fix --ignore-path .gitignore .",
+ "format": "prettier --write --cache --plugin-search-dir=. .",
+ "pre-commit": "lint-staged",
+ "postinstall": "husky install; svelte-kit sync",
+ "test:unit": "vitest",
+ "test:ui": "vitest --ui",
+ "test:coverage": "vitest run --coverage",
+ "test:browser": "cypress run",
+ "test": "test:unit && test:browser",
+ "cy": "cypress open"
+ },
+ "devDependencies": {
+ "@cypress/snapshot": "2.1.7",
+ "@sveltejs/adapter-static": "1.0.0-next.46",
+ "@sveltejs/kit": "1.0.0-next.522",
+ "@testing-library/jest-dom": "5.16.5",
+ "@testing-library/svelte": "3.2.2",
+ "@types/pako": "2.0.0",
+ "@types/uuid": "8.3.4",
+ "@typescript-eslint/eslint-plugin": "5.42.0",
+ "@typescript-eslint/parser": "5.42.0",
+ "@vitest/ui": "0.24.3",
+ "autoprefixer": "10.4.12",
+ "c8": "7.12.0",
+ "chai": "4.3.6",
+ "cssnano": "5.1.13",
+ "cy-verify-downloads": "0.1.11",
+ "cypress": "10.11.0",
+ "cypress-localstorage-commands": "2.2.1",
+ "eslint": "8.26.0",
+ "eslint-config-prettier": "8.5.0",
+ "eslint-plugin-cypress": "2.12.1",
+ "eslint-plugin-es": "4.1.0",
+ "eslint-plugin-postcss-modules": "2.0.0",
+ "eslint-plugin-svelte3": "4.0.0",
+ "eslint-plugin-tailwindcss": "3.6.2",
+ "eslint-plugin-vitest": "0.0.11",
+ "esserializer": "1.3.2",
+ "husky": "8.0.1",
+ "jsdom": "20.0.1",
+ "lint-staged": "13.0.3",
+ "node-html-parser": "6.1.1",
+ "postcss": "8.4.18",
+ "postcss-load-config": "4.0.1",
+ "prettier": "2.7.1",
+ "prettier-plugin-svelte": "2.8.0",
+ "svelte": "3.52.0",
+ "svelte-preprocess": "4.10.7",
+ "tailwindcss": "3.2.1",
+ "tslib": "2.4.0",
+ "typescript": "4.8.4",
+ "vite": "3.2.2",
+ "vitest": "0.24.3"
+ },
+ "dependencies": {
+ "analytics": "0.8.1",
+ "analytics-plugin-plausible": "0.0.6",
+ "daisyui": "2.37.0",
+ "js-base64": "3.7.2",
+ "mermaid": "9.2.0",
+ "moment": "2.29.4",
+ "monaco-editor": "0.34.1",
+ "monaco-mermaid": "1.0.6",
+ "pako": "2.0.4",
+ "random-word-slugs": "0.1.6",
+ "svg-pan-zoom": "3.6.1",
+ "uuid": "9.0.0"
+ },
+ "lint-staged": {
+ "*.{ts,svelte,js,css,md,json}": [
+ "prettier --plugin-search-dir=. --write",
+ "eslint --ignore-path .gitignore "
+ ]
+ },
+ "volta": {
+ "node": "18.12.0",
+ "yarn": "1.22.19"
+ },
+ "engines": {
+ "node": ">=16.7"
+ }
}
diff --git a/postcss.config.cjs b/postcss.config.cjs
index 483734f1..efb41d9e 100644
--- a/postcss.config.cjs
+++ b/postcss.config.cjs
@@ -6,18 +6,18 @@ const mode = process.env.NODE_ENV;
const dev = mode === 'development';
module.exports = {
- plugins: [
- // Some plugins, like postcss-nested, need to run before Tailwind
+ plugins: [
+ // Some plugins, like postcss-nested, need to run before Tailwind
- tailwindcss,
+ tailwindcss,
- // But others, like autoprefixer, need to run after
+ // But others, like autoprefixer, need to run after
- autoprefixer,
+ autoprefixer,
- !dev &&
- cssnano({
- preset: 'default'
- })
- ]
+ !dev &&
+ cssnano({
+ preset: 'default'
+ })
+ ]
};
diff --git a/renovate.json b/renovate.json
index 619bbb26..1413a3ce 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,24 +1,24 @@
{
- "extends": [
- "config:base",
- ":rebaseStalePrs",
- "group:allNonMajor",
- "schedule:earlyMondays",
- ":automergeMinor",
- ":automergeTesters",
- ":automergeLinters",
- ":automergeTypes",
- ":automergePatch"
- ],
- "packageRules": [
- {
- "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
- "automerge": true
- }
- ],
- "dependencyDashboard": true,
- "major": {
- "dependencyDashboardApproval": true
- },
- "dependencyDashboardAutoclose": true
+ "extends": [
+ "config:base",
+ ":rebaseStalePrs",
+ "group:allNonMajor",
+ "schedule:earlyMondays",
+ ":automergeMinor",
+ ":automergeTesters",
+ ":automergeLinters",
+ ":automergeTypes",
+ ":automergePatch"
+ ],
+ "packageRules": [
+ {
+ "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
+ "automerge": true
+ }
+ ],
+ "dependencyDashboard": true,
+ "major": {
+ "dependencyDashboardApproval": true
+ },
+ "dependencyDashboardAutoclose": true
}
diff --git a/src/app.html b/src/app.html
index 4224cc58..3af662f7 100644
--- a/src/app.html
+++ b/src/app.html
@@ -1,57 +1,57 @@
-
-
-
- Online FlowChart & Diagrams Editor - Mermaid Live Editor
-
-
-
-
-
-
-
-
-
-
-
-
-
- %sveltekit.head%
-
-
- %sveltekit.body%
-
+
+
+
+ Online FlowChart & Diagrams Editor - Mermaid Live Editor
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
diff --git a/src/app.postcss b/src/app.postcss
index 1b5c3824..888c737e 100644
--- a/src/app.postcss
+++ b/src/app.postcss
@@ -3,8 +3,8 @@
@tailwind utilities;
.input {
- @apply flex-1 border-primary border-solid border-2 rounded;
+ @apply flex-1 border-primary border-solid border-2 rounded;
}
.action-btn {
- @apply btn btn-primary;
+ @apply btn btn-primary;
}
diff --git a/src/env.d.ts b/src/env.d.ts
index 571447df..e3cf245e 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -1,14 +1,14 @@
///
interface ImportMetaEnv {
- readonly MERMAID_RENDERER_URL: string;
- readonly MERMAID_KROKI_RENDERER_URL: string;
- readonly MERMAID_CDN_URL: string;
- readonly MERMAID_BASE_URL: string;
- readonly MERMAID_LOCAL: boolean;
- // more env variables...
+ readonly MERMAID_RENDERER_URL: string;
+ readonly MERMAID_KROKI_RENDERER_URL: string;
+ readonly MERMAID_CDN_URL: string;
+ readonly MERMAID_BASE_URL: string;
+ readonly MERMAID_LOCAL: boolean;
+ // more env variables...
}
interface ImportMeta {
- readonly env: ImportMetaEnv;
+ readonly env: ImportMetaEnv;
}
diff --git a/src/global.d.ts b/src/global.d.ts
index 17fa2e24..81ca3d20 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -6,10 +6,10 @@
import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers';
declare global {
- namespace jest {
- interface Matchers
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- extends TestingLibraryMatchers {}
- }
+ namespace jest {
+ interface Matchers
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ extends TestingLibraryMatchers {}
+ }
}
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
index 75d6e91a..49af22b0 100644
--- a/src/hooks.server.ts
+++ b/src/hooks.server.ts
@@ -1,6 +1,6 @@
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
- const response = await resolve(event, {});
- return response;
+ const response = await resolve(event, {});
+ return response;
};
diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte
index e1634e10..ed2acfd3 100644
--- a/src/lib/components/actions.svelte
+++ b/src/lib/components/actions.svelte
@@ -1,260 +1,260 @@
-
- {#if isClipboardAvailable()}
-
- {/if}
-
-
-
-
-
-
-
-
-
-
-
+
+ {#if isClipboardAvailable()}
+
+ {/if}
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
- {#if isNetlify}
-
- {/if}
-
+
+
+
+
+ {#if isNetlify}
+
+ {/if}
+
diff --git a/src/lib/components/card/card.svelte b/src/lib/components/card/card.svelte
index 24ea56f4..ce1c0ad7 100644
--- a/src/lib/components/card/card.svelte
+++ b/src/lib/components/card/card.svelte
@@ -1,31 +1,31 @@
-
(isOpen = !isOpen)}
- on:keypress={() => (isOpen = !isOpen)}>
-
-
- {#if isOpen}
-
-
-
- {/if}
+
(isOpen = !isOpen)}
+ on:keypress={() => (isOpen = !isOpen)}>
+
+
+ {#if isOpen}
+
+
+
+ {/if}
diff --git a/src/lib/components/card/card.test.ts b/src/lib/components/card/card.test.ts
index 98ffccd3..764cad64 100644
--- a/src/lib/components/card/card.test.ts
+++ b/src/lib/components/card/card.test.ts
@@ -3,22 +3,22 @@ import { describe, expect, it, afterEach } from 'vitest';
import Card from './card.svelte';
describe('card.svelte', () => {
- // TODO: @testing-library/svelte claims to add this automatically but it doesn't work without explicit afterEach
- afterEach(() => cleanup());
+ // TODO: @testing-library/svelte claims to add this automatically but it doesn't work without explicit afterEach
+ afterEach(() => cleanup());
- it('mounts', () => {
- const { container } = render(Card, {
- title: 'TabTest',
- tabs: [
- { id: 't1', title: 'title1' },
- { id: 't2', title: 'title2' }
- ]
- });
- expect(container).toBeTruthy();
- expect(container).toHaveTextContent('TabTest');
- expect(container).toHaveTextContent('title1');
- expect(container).toHaveTextContent('title2');
- expect(container).not.toHaveTextContent('title3');
- expect(container.innerHTML).toMatchSnapshot();
- });
+ it('mounts', () => {
+ const { container } = render(Card, {
+ title: 'TabTest',
+ tabs: [
+ { id: 't1', title: 'title1' },
+ { id: 't2', title: 'title2' }
+ ]
+ });
+ expect(container).toBeTruthy();
+ expect(container).toHaveTextContent('TabTest');
+ expect(container).toHaveTextContent('title1');
+ expect(container).toHaveTextContent('title2');
+ expect(container).not.toHaveTextContent('title3');
+ expect(container.innerHTML).toMatchSnapshot();
+ });
});
diff --git a/src/lib/components/card/tabs.svelte b/src/lib/components/card/tabs.svelte
index 433d6628..3c7c8f9d 100644
--- a/src/lib/components/card/tabs.svelte
+++ b/src/lib/components/card/tabs.svelte
@@ -1,52 +1,52 @@
-
(isOpen = !isOpen)}
- on:keypress|stopPropagation={() => (isOpen = !isOpen)}>
- {#if isCloseable}
-
- {/if}
- {title}
- {#if isOpen && tabs}
-
- {/if}
+
(isOpen = !isOpen)}
+ on:keypress|stopPropagation={() => (isOpen = !isOpen)}>
+ {#if isCloseable}
+
+ {/if}
+ {title}
+ {#if isOpen && tabs}
+
+ {/if}
diff --git a/src/lib/components/editor.svelte b/src/lib/components/editor.svelte
index fbf4ec49..b84683c8 100644
--- a/src/lib/components/editor.svelte
+++ b/src/lib/components/editor.svelte
@@ -1,115 +1,115 @@
diff --git a/src/lib/components/history/history.svelte b/src/lib/components/history/history.svelte
index 35d3da43..37527bcc 100644
--- a/src/lib/components/history/history.svelte
+++ b/src/lib/components/history/history.svelte
@@ -1,192 +1,192 @@
-
-
- {#if $historyStore.length > 0}
-
- {/if}
- |
-
- {#if $historyModeStore !== 'loader'}
-
- {/if}
-
-
- {#if $historyStore.length > 0}
- {#each $historyStore as { id, state, time, name, url, type }}
- -
-
-
-
- {#if url}
-
{name}
- {:else}
-
{name}
- {/if}
-
{relativeTime(time)}
-
-
-
-
- {#if type !== 'loader'}
-
- {/if}
-
-
-
- {/each}
- {:else}
-
- No items in History
- Click the Save button to save current state and restore it later.
- Timeline will automatically be saved every minute.
-
- {/if}
-
+
+
+ {#if $historyStore.length > 0}
+
+ {/if}
+ |
+
+ {#if $historyModeStore !== 'loader'}
+
+ {/if}
+
+
+ {#if $historyStore.length > 0}
+ {#each $historyStore as { id, state, time, name, url, type }}
+ -
+
+
+
+ {#if url}
+
{name}
+ {:else}
+
{name}
+ {/if}
+
{relativeTime(time)}
+
+
+
+
+ {#if type !== 'loader'}
+
+ {/if}
+
+
+
+ {/each}
+ {:else}
+
+ No items in History
+ Click the Save button to save current state and restore it later.
+ Timeline will automatically be saved every minute.
+
+ {/if}
+
diff --git a/src/lib/components/history/history.test.ts b/src/lib/components/history/history.test.ts
index 2d329e37..d5a0242b 100644
--- a/src/lib/components/history/history.test.ts
+++ b/src/lib/components/history/history.test.ts
@@ -1,120 +1,120 @@
import type { HistoryEntry } from '$lib/types';
import { describe, it, expect } from 'vitest';
import {
- addHistoryEntry,
- injectHistoryIDs,
- clearHistoryData,
- historyModeStore,
- historyStore
+ addHistoryEntry,
+ injectHistoryIDs,
+ clearHistoryData,
+ historyModeStore,
+ historyStore
} from './history';
import { defaultState } from '../../util/state';
import { get } from 'svelte/store';
describe('history', () => {
- it('should handle saving individual history entry', () => {
- expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
- expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
+ it('should handle saving individual history entry', () => {
+ expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
+ expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
- addHistoryEntry({
- state: defaultState,
- time: 12345,
- type: 'manual'
- });
+ addHistoryEntry({
+ state: defaultState,
+ time: 12345,
+ type: 'manual'
+ });
- const [manualEntry]: HistoryEntry[] = JSON.parse(
- window.localStorage.getItem('manualHistoryStore')
- );
+ const [manualEntry]: HistoryEntry[] = JSON.parse(
+ window.localStorage.getItem('manualHistoryStore')
+ );
- expect(manualEntry.time).toBe(12345);
- expect(manualEntry.type).toBe('manual');
- expect(manualEntry.name).not.toBeNull();
- expect(manualEntry.state).not.toBeNull();
+ expect(manualEntry.time).toBe(12345);
+ expect(manualEntry.type).toBe('manual');
+ expect(manualEntry.name).not.toBeNull();
+ expect(manualEntry.state).not.toBeNull();
- addHistoryEntry({
- state: defaultState,
- time: 54321,
- type: 'auto'
- });
+ addHistoryEntry({
+ state: defaultState,
+ time: 54321,
+ type: 'auto'
+ });
- const [autoEntry]: HistoryEntry[] = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
+ const [autoEntry]: HistoryEntry[] = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
- expect(autoEntry.time).toBe(54321);
- expect(autoEntry.type).toBe('auto');
- expect(autoEntry.name).not.toBeNull();
- expect(autoEntry.state).not.toBeNull();
+ expect(autoEntry.time).toBe(54321);
+ expect(autoEntry.type).toBe('auto');
+ expect(autoEntry.name).not.toBeNull();
+ expect(autoEntry.state).not.toBeNull();
- historyModeStore.set('manual');
- clearHistoryData();
- historyModeStore.set('auto');
- clearHistoryData();
- expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
- expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
- });
+ historyModeStore.set('manual');
+ clearHistoryData();
+ historyModeStore.set('auto');
+ clearHistoryData();
+ expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
+ expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
+ });
- it('should clear history entries', () => {
- addHistoryEntry({
- state: defaultState,
- time: 12345,
- type: 'manual'
- });
- addHistoryEntry({
- state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
- time: 123456,
- type: 'manual'
- });
+ it('should clear history entries', () => {
+ addHistoryEntry({
+ state: defaultState,
+ time: 12345,
+ type: 'manual'
+ });
+ addHistoryEntry({
+ state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
+ time: 123456,
+ type: 'manual'
+ });
- historyModeStore.set('manual');
- const store: HistoryEntry[] = get(historyStore);
- expect(store.length).toBe(2);
- clearHistoryData(store[1].id);
- expect(get(historyStore).length).toBe(1);
- clearHistoryData();
- expect(get(historyStore).length).toBe(0);
+ historyModeStore.set('manual');
+ const store: HistoryEntry[] = get(historyStore);
+ expect(store.length).toBe(2);
+ clearHistoryData(store[1].id);
+ expect(get(historyStore).length).toBe(1);
+ clearHistoryData();
+ expect(get(historyStore).length).toBe(0);
- historyModeStore.set('auto');
- addHistoryEntry({
- state: defaultState,
- time: 54321,
- type: 'auto'
- });
- addHistoryEntry({
- state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
- time: 654321,
- type: 'auto'
- });
- expect(get(historyStore).length).toBe(2);
- clearHistoryData();
- expect(get(historyStore).length).toBe(0);
- // Test calling when history is empty
- clearHistoryData();
- expect(get(historyStore).length).toBe(0);
- });
+ historyModeStore.set('auto');
+ addHistoryEntry({
+ state: defaultState,
+ time: 54321,
+ type: 'auto'
+ });
+ addHistoryEntry({
+ state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
+ time: 654321,
+ type: 'auto'
+ });
+ expect(get(historyStore).length).toBe(2);
+ clearHistoryData();
+ expect(get(historyStore).length).toBe(0);
+ // Test calling when history is empty
+ clearHistoryData();
+ expect(get(historyStore).length).toBe(0);
+ });
});
describe('history migration', () => {
- it('should inject history IDs as migration', () => {
- window.localStorage.setItem(
- 'manualHistoryStore',
- '[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
- );
- window.localStorage.setItem(
- 'autoHistoryStore',
- '[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
- );
- let manualHistoryStore: HistoryEntry[] = JSON.parse(
- window.localStorage.getItem('manualHistoryStore')
- );
- let autoHistoryStore: HistoryEntry[] = JSON.parse(
- window.localStorage.getItem('autoHistoryStore')
- );
- expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
- expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
+ it('should inject history IDs as migration', () => {
+ window.localStorage.setItem(
+ 'manualHistoryStore',
+ '[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
+ );
+ window.localStorage.setItem(
+ 'autoHistoryStore',
+ '[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
+ );
+ let manualHistoryStore: HistoryEntry[] = JSON.parse(
+ window.localStorage.getItem('manualHistoryStore')
+ );
+ let autoHistoryStore: HistoryEntry[] = JSON.parse(
+ window.localStorage.getItem('autoHistoryStore')
+ );
+ expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
+ expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
- injectHistoryIDs();
+ injectHistoryIDs();
- manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
- autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
- expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
- expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
- });
+ manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
+ autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
+ expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
+ expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
+ });
});
diff --git a/src/lib/components/history/history.ts b/src/lib/components/history/history.ts
index e5f53c2d..20f19ba3 100644
--- a/src/lib/components/history/history.ts
+++ b/src/lib/components/history/history.ts
@@ -9,133 +9,133 @@ import { logEvent } from '$lib/util/stats';
const MAX_AUTO_HISTORY_LENGTH = 30;
export const historyModeStore: Writable = persist(
- writable('manual'),
- localStorage(),
- 'autoHistoryMode'
+ writable('manual'),
+ localStorage(),
+ 'autoHistoryMode'
);
const autoHistoryStore: Writable = persist(
- writable([]),
- localStorage(),
- 'autoHistoryStore'
+ writable([]),
+ localStorage(),
+ 'autoHistoryStore'
);
const manualHistoryStore: Writable = persist(
- writable([]),
- localStorage(),
- 'manualHistoryStore'
+ writable([]),
+ localStorage(),
+ 'manualHistoryStore'
);
export const loaderHistoryStore: Writable = writable([] as HistoryEntry[]);
export const historyStore: Readable = derived(
- [historyModeStore, autoHistoryStore, manualHistoryStore, loaderHistoryStore],
- ([historyMode, autoHistories, manualHistories, loadedHistories], set) => {
- if (historyMode === 'auto') {
- set(autoHistories);
- } else if (historyMode === 'manual') {
- set(manualHistories);
- } else if (historyMode === 'loader') {
- set(loadedHistories);
- } else {
- set(autoHistories);
- }
- }
+ [historyModeStore, autoHistoryStore, manualHistoryStore, loaderHistoryStore],
+ ([historyMode, autoHistories, manualHistories, loadedHistories], set) => {
+ if (historyMode === 'auto') {
+ set(autoHistories);
+ } else if (historyMode === 'manual') {
+ set(manualHistories);
+ } else if (historyMode === 'loader') {
+ set(loadedHistories);
+ } else {
+ set(autoHistories);
+ }
+ }
);
export const addHistoryEntry = (entryToAdd: Optional): void => {
- const entry: HistoryEntry = {
- ...entryToAdd,
- id: uuidV4()
- };
+ const entry: HistoryEntry = {
+ ...entryToAdd,
+ id: uuidV4()
+ };
- if (entry.type === 'loader') {
- loaderHistoryStore.update((entries) => [entry, ...entries]);
- return;
- }
+ if (entry.type === 'loader') {
+ loaderHistoryStore.update((entries) => [entry, ...entries]);
+ return;
+ }
- if (!entry.name) {
- entry.name = generateSlug(2);
- }
+ if (!entry.name) {
+ entry.name = generateSlug(2);
+ }
- if (entry.type === 'auto') {
- autoHistoryStore.update((entries) => {
- if (entries.length >= MAX_AUTO_HISTORY_LENGTH) {
- entries = entries.slice(0, MAX_AUTO_HISTORY_LENGTH - 1);
- }
- return [entry, ...entries];
- });
- } else if (entry.type === 'manual') {
- manualHistoryStore.update((entries) => [entry, ...entries]);
- logEvent('history', { action: 'save' });
- }
+ if (entry.type === 'auto') {
+ autoHistoryStore.update((entries) => {
+ if (entries.length >= MAX_AUTO_HISTORY_LENGTH) {
+ entries = entries.slice(0, MAX_AUTO_HISTORY_LENGTH - 1);
+ }
+ return [entry, ...entries];
+ });
+ } else if (entry.type === 'manual') {
+ manualHistoryStore.update((entries) => [entry, ...entries]);
+ logEvent('history', { action: 'save' });
+ }
};
export const clearHistoryData = (idToClear?: string): void => {
- (get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => {
- if (get(historyModeStore) !== 'loader') {
- entries = entries.filter(({ id }) => idToClear && id != idToClear);
- logEvent('history', { action: 'clear', type: idToClear ? 'single' : 'all' });
- }
- return entries;
- });
+ (get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => {
+ if (get(historyModeStore) !== 'loader') {
+ entries = entries.filter(({ id }) => idToClear && id != idToClear);
+ logEvent('history', { action: 'clear', type: idToClear ? 'single' : 'all' });
+ }
+ return entries;
+ });
};
export const getPreviousState = (auto: boolean): string => {
- const entries = get(auto ? autoHistoryStore : manualHistoryStore);
- if (entries.length > 0) {
- return JSON.stringify(entries[0].state);
- }
- return '';
+ const entries = get(auto ? autoHistoryStore : manualHistoryStore);
+ if (entries.length > 0) {
+ return JSON.stringify(entries[0].state);
+ }
+ return '';
};
export const restoreHistory = (data: HistoryEntry[]) => {
- const entries = data.filter(validateEntry);
- const invalidEntryCount = data.length - entries.length;
- if (invalidEntryCount > 0) {
- console.error(`${invalidEntryCount} invalid history entries were removed.`);
- console.error(data);
- }
- if (entries.length > 0) {
- let entryCount = 0;
- (entries[0].type === 'auto' ? autoHistoryStore : manualHistoryStore).update((existing) => {
- const existingIDs = new Set(existing.map(({ id }) => id));
- const newEntries = entries.filter(({ id }) => !existingIDs.has(id));
- entryCount = newEntries.length;
- const combined = [...existing, ...newEntries];
- combined.sort((a, b) => b.time - a.time);
- return combined;
- });
+ const entries = data.filter(validateEntry);
+ const invalidEntryCount = data.length - entries.length;
+ if (invalidEntryCount > 0) {
+ console.error(`${invalidEntryCount} invalid history entries were removed.`);
+ console.error(data);
+ }
+ if (entries.length > 0) {
+ let entryCount = 0;
+ (entries[0].type === 'auto' ? autoHistoryStore : manualHistoryStore).update((existing) => {
+ const existingIDs = new Set(existing.map(({ id }) => id));
+ const newEntries = entries.filter(({ id }) => !existingIDs.has(id));
+ entryCount = newEntries.length;
+ const combined = [...existing, ...newEntries];
+ combined.sort((a, b) => b.time - a.time);
+ return combined;
+ });
- alert(
- `${entryCount} entries restored. ${invalidEntryCount} invalid, ${
- entries.length - entryCount
- } duplicates.`
- );
- logEvent('history', {
- action: 'restore',
- success: entryCount,
- invalid: invalidEntryCount,
- duplicates: entries.length - entryCount
- });
- } else {
- alert('No valid entries found.');
- }
+ alert(
+ `${entryCount} entries restored. ${invalidEntryCount} invalid, ${
+ entries.length - entryCount
+ } duplicates.`
+ );
+ logEvent('history', {
+ action: 'restore',
+ success: entryCount,
+ invalid: invalidEntryCount,
+ duplicates: entries.length - entryCount
+ });
+ } else {
+ alert('No valid entries found.');
+ }
};
export const injectHistoryIDs = (): void => {
- const setIDs = (entries: HistoryEntry[]) => {
- for (const entry of entries) {
- if (!entry.id) {
- entry.id = uuidV4();
- }
- }
- return entries;
- };
- autoHistoryStore.update(setIDs);
- manualHistoryStore.update(setIDs);
+ const setIDs = (entries: HistoryEntry[]) => {
+ for (const entry of entries) {
+ if (!entry.id) {
+ entry.id = uuidV4();
+ }
+ }
+ return entries;
+ };
+ autoHistoryStore.update(setIDs);
+ manualHistoryStore.update(setIDs);
};
const validateEntry = (entry: HistoryEntry): boolean => {
- return entry.type && entry.state && entry.time && true;
+ return entry.type && entry.state && entry.time && true;
};
diff --git a/src/lib/components/navbar.svelte b/src/lib/components/navbar.svelte
index 99e3a309..9a179bb9 100644
--- a/src/lib/components/navbar.svelte
+++ b/src/lib/components/navbar.svelte
@@ -1,82 +1,82 @@
-
-
-
+
+
+
-
-
+
+
diff --git a/src/lib/components/preset.svelte b/src/lib/components/preset.svelte
index ec36c2f2..c1d466b5 100644
--- a/src/lib/components/preset.svelte
+++ b/src/lib/components/preset.svelte
@@ -1,21 +1,21 @@
-
- {#each diagramOrder as sample}
-
- {/each}
-
+
+ {#each diagramOrder as sample}
+
+ {/each}
+
diff --git a/src/lib/components/theme.svelte b/src/lib/components/theme.svelte
index 8c786aa3..2e860077 100644
--- a/src/lib/components/theme.svelte
+++ b/src/lib/components/theme.svelte
@@ -1,64 +1,64 @@
diff --git a/src/lib/components/view.svelte b/src/lib/components/view.svelte
index 7807be04..dc5cfc6d 100644
--- a/src/lib/components/view.svelte
+++ b/src/lib/components/view.svelte
@@ -1,157 +1,157 @@
{#if (error && $stateStore.error instanceof Error) || outOfSync}
-
- {#if error}
- {$stateStore.error}
- {:else}
- Diagram out of sync.
- Press (Sync button) or {cmdKey} + Enter to sync.
- {/if}
-
+
+ {#if error}
+ {$stateStore.error}
+ {:else}
+ Diagram out of sync.
+ Press (Sync button) or {cmdKey} + Enter to sync.
+ {/if}
+
{/if}
diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts
index 04da50b8..30f94b9c 100644
--- a/src/lib/types.d.ts
+++ b/src/lib/types.d.ts
@@ -3,81 +3,81 @@
* inside `global.d.ts` and removing `export` keyword
*/
export interface Locals {
- userid: string;
+ userid: string;
}
export interface MarkerData {
- severity: number;
- message: string;
- source?: string;
- startLineNumber: number;
- startColumn: number;
- endLineNumber: number;
- endColumn: number;
+ severity: number;
+ message: string;
+ source?: string;
+ startLineNumber: number;
+ startColumn: number;
+ endLineNumber: number;
+ endColumn: number;
}
export interface TabEvents {
- select: Tab;
+ select: Tab;
}
export interface Tab {
- id: string;
- title: string;
- icon: string;
+ id: string;
+ title: string;
+ icon: string;
}
export interface State {
- code: string;
- mermaid: string;
- updateDiagram: boolean;
- autoSync: boolean;
- editorMode?: EditorMode;
- panZoom?: boolean;
- pan?: { x: number; y: number };
- zoom?: number;
- loader?: LoaderConfig;
+ code: string;
+ mermaid: string;
+ updateDiagram: boolean;
+ autoSync: boolean;
+ editorMode?: EditorMode;
+ panZoom?: boolean;
+ pan?: { x: number; y: number };
+ zoom?: number;
+ loader?: LoaderConfig;
}
export interface ValidatedState extends State {
- editorMode: EditorMode;
- error: unknown;
- errorMarkers: MarkerData[];
- serialized: string;
+ editorMode: EditorMode;
+ error: unknown;
+ errorMarkers: MarkerData[];
+ serialized: string;
}
export interface GistLoaderConfig {
- url: string;
+ url: string;
}
export interface LoadingState {
- loading: boolean;
- message?: string;
+ loading: boolean;
+ message?: string;
}
export interface FileLoaderConfig {
- codeURL: string;
- configURL?: string;
+ codeURL: string;
+ configURL?: string;
}
export interface LoaderConfig {
- type: 'gist' | 'files';
- config: GistLoaderConfig | FileLoaderConfig;
+ type: 'gist' | 'files';
+ config: GistLoaderConfig | FileLoaderConfig;
}
export type HistoryType = 'auto' | 'manual' | 'loader';
export type HistoryEntry = { id: string; state: State; time: number; url?: string } & (
- | {
- type: 'loader';
- name: string;
- }
- | {
- type: HistoryType;
- name?: string;
- }
+ | {
+ type: 'loader';
+ name: string;
+ }
+ | {
+ type: HistoryType;
+ name?: string;
+ }
);
export interface DocConfig {
- [key: string]: {
- code: string;
- config?: string;
- };
+ [key: string]: {
+ code: string;
+ config?: string;
+ };
}
export type EditorMode = 'code' | 'config';
diff --git a/src/lib/util/env.ts b/src/lib/util/env.ts
index f5738f65..2c14a52e 100644
--- a/src/lib/util/env.ts
+++ b/src/lib/util/env.ts
@@ -1,9 +1,9 @@
export const env = {
- rendererUrl: import.meta.env.MERMAID_RENDERER_URL ?? 'https://mermaid.ink',
- krokiRendererUrl: import.meta.env.MERMAID_KROKI_RENDERER_URL ?? 'https://kroki.io',
- mermaidCDNUrl: import.meta.env.MERMAID_CDN_URL ?? 'https://unpkg.com/@mermaid-js',
- mermaidBaseURL: import.meta.env.MERMAID_BASE_URL ?? 'http://localhost:9000',
- useLocalMermaid: import.meta.env.MERMAID_LOCAL ?? false,
- isDev: import.meta.env.DEV,
- baseURL: import.meta.env.BASE_URL
+ rendererUrl: import.meta.env.MERMAID_RENDERER_URL ?? 'https://mermaid.ink',
+ krokiRendererUrl: import.meta.env.MERMAID_KROKI_RENDERER_URL ?? 'https://kroki.io',
+ mermaidCDNUrl: import.meta.env.MERMAID_CDN_URL ?? 'https://unpkg.com/@mermaid-js',
+ mermaidBaseURL: import.meta.env.MERMAID_BASE_URL ?? 'http://localhost:9000',
+ useLocalMermaid: import.meta.env.MERMAID_LOCAL ?? false,
+ isDev: import.meta.env.DEV,
+ baseURL: import.meta.env.BASE_URL
};
diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts
index 6304f9c5..f2cb7ed9 100644
--- a/src/lib/util/fileLoaders/gist.ts
+++ b/src/lib/util/fileLoaders/gist.ts
@@ -10,98 +10,98 @@ const codeFileName = 'code.mmd';
const configFileName = 'config.json';
const isValidGist = (files: any): boolean => {
- return codeFileName in files;
+ return codeFileName in files;
};
const getFileContent = async (file: any): Promise => {
- if (file.truncated) {
- return await (await fetch(file.raw_url)).text();
- }
- return file.content;
+ if (file.truncated) {
+ return await (await fetch(file.raw_url)).text();
+ }
+ return file.content;
};
interface GistData {
- code: string;
- config?: string;
- author: string;
- time: number;
- version: string;
- url: string;
+ code: string;
+ config?: string;
+ author: string;
+ time: number;
+ version: string;
+ url: string;
}
const getGistData = async (gistURL: string): Promise => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const { html_url, files, history } = await (
- await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
- ).json();
- if (isValidGist(files)) {
- const code = await getFileContent(files[codeFileName]);
- let config: string;
- if (configFileName in files) {
- config = await getFileContent(files[configFileName]);
- }
- const currentItem = history[0];
- return {
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- url: `${html_url}/${currentItem.version}`,
- code,
- config,
- author: currentItem.user.login,
- time: new Date(currentItem.committed_at).getTime(),
- version: (currentItem.version as string).slice(-7)
- };
- } else {
- throw 'Invalid gist provided';
- }
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const { html_url, files, history } = await (
+ await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
+ ).json();
+ if (isValidGist(files)) {
+ const code = await getFileContent(files[codeFileName]);
+ let config: string;
+ if (configFileName in files) {
+ config = await getFileContent(files[configFileName]);
+ }
+ const currentItem = history[0];
+ return {
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ url: `${html_url}/${currentItem.version}`,
+ code,
+ config,
+ author: currentItem.user.login,
+ time: new Date(currentItem.committed_at).getTime(),
+ version: (currentItem.version as string).slice(-7)
+ };
+ } else {
+ throw 'Invalid gist provided';
+ }
};
const getStateFromGist = (gist: GistData, gistURL: string = gist.url): State => {
- const state: State = {
- ...defaultState,
- code: gist.code,
- loader: {
- type: 'gist',
- config: {
- url: gistURL
- }
- }
- };
- gist.config && (state.mermaid = gist.config);
- return state;
+ const state: State = {
+ ...defaultState,
+ code: gist.code,
+ loader: {
+ type: 'gist',
+ config: {
+ url: gistURL
+ }
+ }
+ };
+ gist.config && (state.mermaid = gist.config);
+ return state;
};
export const loadGistData = async (gistURL: string): Promise => {
- try {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const { history } = await (
- await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
- ).json();
- const gistHistory: GistData[] = [];
- for (const entry of history) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const data: GistData = await getGistData(entry.url).catch(() => undefined);
- data && gistHistory.push(data);
- }
- if (gistHistory.length === 0) {
- throw 'Invalid gist provided';
- }
- gistHistory.reverse();
- const state = getStateFromGist(gistHistory.slice(-1).pop(), gistURL);
- for (const gist of gistHistory) {
- addHistoryEntry({
- state: getStateFromGist(gist),
- time: gist.time,
- type: 'loader',
- url: gist.url,
- name: `${gist.author} v${gist.version}`
- });
- }
- return state;
- } catch (err) {
- console.error(err);
- }
+ try {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const { history } = await (
+ await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
+ ).json();
+ const gistHistory: GistData[] = [];
+ for (const entry of history) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const data: GistData = await getGistData(entry.url).catch(() => undefined);
+ data && gistHistory.push(data);
+ }
+ if (gistHistory.length === 0) {
+ throw 'Invalid gist provided';
+ }
+ gistHistory.reverse();
+ const state = getStateFromGist(gistHistory.slice(-1).pop(), gistURL);
+ for (const gist of gistHistory) {
+ addHistoryEntry({
+ state: getStateFromGist(gist),
+ time: gist.time,
+ type: 'loader',
+ url: gist.url,
+ name: `${gist.author} v${gist.version}`
+ });
+ }
+ return state;
+ } catch (err) {
+ console.error(err);
+ }
};
diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts
index 9e8df432..e40a988f 100644
--- a/src/lib/util/fileLoaders/loader.ts
+++ b/src/lib/util/fileLoaders/loader.ts
@@ -2,58 +2,58 @@ import { loadGistData } from './gist';
import { updateCodeStore, defaultState } from '../state';
import type { Loader, State } from '$lib/types';
const loaders: Record = {
- gist: loadGistData
+ gist: loadGistData
};
export const loadDataFromUrl = async (): Promise => {
- const searchParams = new URLSearchParams(window.location.search);
- let state: Partial = defaultState;
- let code: string, config: string;
- let loaded = false;
- const codeURL: string = searchParams.get('code');
- const configURL: string = searchParams.get('config');
+ const searchParams = new URLSearchParams(window.location.search);
+ let state: Partial = defaultState;
+ let code: string, config: string;
+ let loaded = false;
+ const codeURL: string = searchParams.get('code');
+ const configURL: string = searchParams.get('config');
- if (codeURL) {
- code = await (await fetch(codeURL)).text();
- loaded = true;
- }
- if (configURL) {
- config = await (await fetch(configURL)).text();
- } else {
- config = defaultState.mermaid;
- }
- if (!code) {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
- for (const [key, value] of searchParams.entries()) {
- if (key in loaders) {
- try {
- state = await loaders[key](value);
- loaded = true;
- break;
- } catch (err) {
- console.error(err);
- }
- }
- }
- } else {
- state = {
- code,
- mermaid: config,
- loader: {
- type: 'files',
- config: {
- codeURL,
- configURL
- }
- }
- };
- }
- loaded &&
- updateCodeStore({
- ...state,
- autoSync: true,
- updateDiagram: true
- });
+ if (codeURL) {
+ code = await (await fetch(codeURL)).text();
+ loaded = true;
+ }
+ if (configURL) {
+ config = await (await fetch(configURL)).text();
+ } else {
+ config = defaultState.mermaid;
+ }
+ if (!code) {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ for (const [key, value] of searchParams.entries()) {
+ if (key in loaders) {
+ try {
+ state = await loaders[key](value);
+ loaded = true;
+ break;
+ } catch (err) {
+ console.error(err);
+ }
+ }
+ }
+ } else {
+ state = {
+ code,
+ mermaid: config,
+ loader: {
+ type: 'files',
+ config: {
+ codeURL,
+ configURL
+ }
+ }
+ };
+ }
+ loaded &&
+ updateCodeStore({
+ ...state,
+ autoSync: true,
+ updateDiagram: true
+ });
};
diff --git a/src/lib/util/loading.ts b/src/lib/util/loading.ts
index 0d483c91..cebe1947 100644
--- a/src/lib/util/loading.ts
+++ b/src/lib/util/loading.ts
@@ -3,18 +3,18 @@ import type { Writable } from 'svelte/store';
import type { LoadingState } from '$lib/types';
const defaultLoading: LoadingState = {
- loading: false
+ loading: false
};
export const loadingStateStore: Writable = writable(defaultLoading);
export const initLoading = async (message: string, task: Promise): Promise => {
- loadingStateStore.set({
- loading: true,
- message
- });
- const result: T = await task;
- loadingStateStore.set({
- loading: false
- });
- return result;
+ loadingStateStore.set({
+ loading: true,
+ message
+ });
+ const result: T = await task;
+ loadingStateStore.set({
+ loading: false
+ });
+ return result;
};
diff --git a/src/lib/util/mermaid.ts b/src/lib/util/mermaid.ts
index 1b95b5f2..5e91c2e8 100644
--- a/src/lib/util/mermaid.ts
+++ b/src/lib/util/mermaid.ts
@@ -5,37 +5,37 @@ import { env } from './env';
const { mermaidBaseURL, mermaidCDNUrl, useLocalMermaid } = env;
const getDiagramURL = (name: string, version: string): string => {
- if (useLocalMermaid) {
- return `${mermaidBaseURL}/${name}-detector.esm.mjs`;
- }
- return `${mermaidCDNUrl}/${name}@${version}/dist/${name}-detector.esm.mjs`;
+ if (useLocalMermaid) {
+ return `${mermaidBaseURL}/${name}-detector.esm.mjs`;
+ }
+ return `${mermaidCDNUrl}/${name}@${version}/dist/${name}-detector.esm.mjs`;
};
const initialize = mermaid.initializeAsync({
- // logLevel: 0,
- lazyLoadedDiagrams: [getDiagramURL('mermaid-mindmap', '9.2.0-rc4')],
- loadExternalDiagramsAtStartup: true
+ // logLevel: 0,
+ lazyLoadedDiagrams: [getDiagramURL('mermaid-mindmap', '9.2.0-rc4')],
+ loadExternalDiagramsAtStartup: true
});
export const init = async () => {
- await initialize;
+ await initialize;
};
export const render = async (
- config: MermaidConfig,
- code: string,
- id: string,
- callback: Parameters[2]
+ config: MermaidConfig,
+ code: string,
+ id: string,
+ callback: Parameters[2]
): Promise => {
- // Should be able to call this multiple times without any issues.
- mermaid.initialize(config);
- // console.log('Rendering', code);
- // mermaid.mermaidAPI.render(id, code, callback);
- await init();
- await mermaid.mermaidAPI.renderAsync(id, code, callback);
+ // Should be able to call this multiple times without any issues.
+ mermaid.initialize(config);
+ // console.log('Rendering', code);
+ // mermaid.mermaidAPI.render(id, code, callback);
+ await init();
+ await mermaid.mermaidAPI.renderAsync(id, code, callback);
};
export const parse = async (code: string): Promise => {
- await init();
- return mermaid.parseAsync(code);
+ await init();
+ return mermaid.parseAsync(code);
};
diff --git a/src/lib/util/migrations.test.ts b/src/lib/util/migrations.test.ts
index b1227960..4b1f79a4 100644
--- a/src/lib/util/migrations.test.ts
+++ b/src/lib/util/migrations.test.ts
@@ -2,33 +2,33 @@ import type { HistoryEntry } from '$lib/types';
import { describe, it, expect, beforeEach } from 'vitest';
describe('migrations', () => {
- beforeEach(() => {
- window.localStorage.setItem(
- 'manualHistoryStore',
- '[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
- );
- window.localStorage.setItem(
- 'autoHistoryStore',
- '[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
- );
- });
+ beforeEach(() => {
+ window.localStorage.setItem(
+ 'manualHistoryStore',
+ '[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
+ );
+ window.localStorage.setItem(
+ 'autoHistoryStore',
+ '[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
+ );
+ });
- it('should migrate from v0 to v1', async () => {
- const { applyMigrations } = await import('./migrations');
- let manualHistoryStore: HistoryEntry[] = JSON.parse(
- window.localStorage.getItem('manualHistoryStore')
- );
- let autoHistoryStore: HistoryEntry[] = JSON.parse(
- window.localStorage.getItem('autoHistoryStore')
- );
- expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
- expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
+ it('should migrate from v0 to v1', async () => {
+ const { applyMigrations } = await import('./migrations');
+ let manualHistoryStore: HistoryEntry[] = JSON.parse(
+ window.localStorage.getItem('manualHistoryStore')
+ );
+ let autoHistoryStore: HistoryEntry[] = JSON.parse(
+ window.localStorage.getItem('autoHistoryStore')
+ );
+ expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
+ expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
- applyMigrations();
+ applyMigrations();
- manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
- autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
- expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
- expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
- });
+ manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
+ autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
+ expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
+ expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
+ });
});
diff --git a/src/lib/util/migrations.ts b/src/lib/util/migrations.ts
index cd57c732..f739bda6 100644
--- a/src/lib/util/migrations.ts
+++ b/src/lib/util/migrations.ts
@@ -4,32 +4,32 @@ import { injectHistoryIDs } from '$lib/components/history/history';
import { logEvent } from './stats';
interface MigrationState {
- version: number;
+ version: number;
}
const migrations: { [key: string]: () => void } = {
- injectHistoryIDs
+ injectHistoryIDs
};
const migrationStore: Writable = persist(
- writable({ version: -1 }),
- localStorage(),
- 'migrations'
+ writable({ version: -1 }),
+ localStorage(),
+ 'migrations'
);
export const applyMigrations = (): void => {
- const { version }: MigrationState = get(migrationStore);
- const allMigrations = Object.entries(migrations);
- if (version === allMigrations.length - 1) {
- return;
- }
- console.log(`Current migration version: v${version}. Migrating to v${allMigrations.length - 1}.`);
- for (let i = version + 1; i < allMigrations.length; i++) {
- const [key, fn] = allMigrations[i];
- console.log(`Applying migration ${i}: ${key}.`);
- fn();
- logEvent('migration', { key });
- migrationStore.set({ version: i });
- }
- logEvent('migration', { status: 'complete', from: version, to: allMigrations.length - 1 });
+ const { version }: MigrationState = get(migrationStore);
+ const allMigrations = Object.entries(migrations);
+ if (version === allMigrations.length - 1) {
+ return;
+ }
+ console.log(`Current migration version: v${version}. Migrating to v${allMigrations.length - 1}.`);
+ for (let i = version + 1; i < allMigrations.length; i++) {
+ const [key, fn] = allMigrations[i];
+ console.log(`Applying migration ${i}: ${key}.`);
+ fn();
+ logEvent('migration', { key });
+ migrationStore.set({ version: i });
+ }
+ logEvent('migration', { status: 'complete', from: version, to: allMigrations.length - 1 });
};
diff --git a/src/lib/util/notify.ts b/src/lib/util/notify.ts
index b9c54a27..3c9e6ded 100644
--- a/src/lib/util/notify.ts
+++ b/src/lib/util/notify.ts
@@ -1,7 +1,7 @@
export const notify = (message: string): void => {
- alert(message);
+ alert(message);
};
export const prompt = (message: string): boolean => {
- return confirm(message);
+ return confirm(message);
};
diff --git a/src/lib/util/persist.ts b/src/lib/util/persist.ts
index ef275f49..cf032b89 100644
--- a/src/lib/util/persist.ts
+++ b/src/lib/util/persist.ts
@@ -29,7 +29,7 @@ import type { Writable } from 'svelte/store';
* Disabled warnings about missing/unavailable storages
*/
export function disableWarnings(): void {
- noWarnings = true;
+ noWarnings = true;
}
/**
* If set to true, no warning will be emitted if the requested Storage is not found.
@@ -46,18 +46,18 @@ const alreadyWarnFor: Array = [];
* @param {string} storageName
*/
const warnStorageNotFound = (storageName) => {
- const isProduction = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
+ const isProduction = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
- if (!noWarnings && alreadyWarnFor.indexOf(storageName) === -1 && !isProduction) {
- let message = `Unable to find the ${storageName}. No data will be persisted.`;
- if (typeof window === 'undefined') {
- message +=
- '\n' +
- 'Are you running on a server? Most of storages are not available while running on a server.';
- }
- console.warn(message);
- alreadyWarnFor.push(storageName);
- }
+ if (!noWarnings && alreadyWarnFor.indexOf(storageName) === -1 && !isProduction) {
+ let message = `Unable to find the ${storageName}. No data will be persisted.`;
+ if (typeof window === 'undefined') {
+ message +=
+ '\n' +
+ 'Are you running on a server? Most of storages are not available while running on a server.';
+ }
+ console.warn(message);
+ alreadyWarnFor.push(storageName);
+ }
};
const allowedClasses = [];
@@ -66,83 +66,83 @@ const allowedClasses = [];
* @param classDef The class to add to the list
*/
export const addSerializableClass = (classDef: () => unknown): void => {
- allowedClasses.push(classDef);
+ allowedClasses.push(classDef);
};
const serialize = (value: unknown): string => ESSerializer.serialize(value);
const deserialize = (value: string): unknown => {
- // @TODO: to remove in the next major
- if (value === 'undefined') {
- return undefined;
- }
+ // @TODO: to remove in the next major
+ if (value === 'undefined') {
+ return undefined;
+ }
- if (value !== null && value !== undefined) {
- try {
- return ESSerializer.deserialize(value, allowedClasses);
- } catch (e) {
- // Do nothing
- // use the value "as is"
- }
- try {
- return JSON.parse(value);
- } catch (e) {
- // Do nothing
- // use the value "as is"
- }
- }
- return value;
+ if (value !== null && value !== undefined) {
+ try {
+ return ESSerializer.deserialize(value, allowedClasses);
+ } catch (e) {
+ // Do nothing
+ // use the value "as is"
+ }
+ try {
+ return JSON.parse(value);
+ } catch (e) {
+ // Do nothing
+ // use the value "as is"
+ }
+ }
+ return value;
};
/**
* A store that keep it's value in time.
*/
export interface PersistentStore extends Writable {
- /**
- * Delete the store value from the persistent storage
- */
- delete(): void;
+ /**
+ * Delete the store value from the persistent storage
+ */
+ delete(): void;
}
/**
* Storage interface
*/
export interface StorageInterface {
- /**
- * Get a value from the storage.
- *
- * If the value doesn't exists in the storage, `null` should be returned.
- * This method MUST be synchronous.
- * @param key The key/name of the value to retrieve
- */
- getValue(key: string): T | null;
+ /**
+ * Get a value from the storage.
+ *
+ * If the value doesn't exists in the storage, `null` should be returned.
+ * This method MUST be synchronous.
+ * @param key The key/name of the value to retrieve
+ */
+ getValue(key: string): T | null;
- /**
- * Save a value in the storage.
- * @param key The key/name of the value to save
- * @param value The value to save
- */
- setValue(key: string, value: T): void;
+ /**
+ * Save a value in the storage.
+ * @param key The key/name of the value to save
+ * @param value The value to save
+ */
+ setValue(key: string, value: T): void;
- /**
- * Remove a value from the storage
- * @param key The key/name of the value to remove
- */
- deleteValue(key: string): void;
+ /**
+ * Remove a value from the storage
+ * @param key The key/name of the value to remove
+ */
+ deleteValue(key: string): void;
}
export interface SelfUpdateStorageInterface extends StorageInterface {
- /**
- * Add a listener to the storage values changes
- * @param {string} key The key to listen
- * @param {(newValue: T) => void} listener The listener callback function
- */
- addListener(key: string, listener: (newValue: T) => void): void;
- /**
- * Remove a listener from the storage values changes
- * @param {string} key The key that was listened
- * @param {(newValue: T) => void} listener The listener callback function to remove
- */
- removeListener(key: string, listener: (newValue: T) => void): void;
+ /**
+ * Add a listener to the storage values changes
+ * @param {string} key The key to listen
+ * @param {(newValue: T) => void} listener The listener callback function
+ */
+ addListener(key: string, listener: (newValue: T) => void): void;
+ /**
+ * Remove a listener from the storage values changes
+ * @param {string} key The key that was listened
+ * @param {(newValue: T) => void} listener The listener callback function to remove
+ */
+ removeListener(key: string, listener: (newValue: T) => void): void;
}
/**
@@ -152,87 +152,87 @@ export interface SelfUpdateStorageInterface extends StorageInterface {
* @param {string} key The name of the data key
*/
export function persist(
- store: Writable,
- storage: StorageInterface,
- key: string
+ store: Writable,
+ storage: StorageInterface,
+ key: string
): PersistentStore {
- const initialValue = storage.getValue(key);
+ const initialValue = storage.getValue(key);
- if (null !== initialValue) {
- store.set(initialValue);
- }
+ if (null !== initialValue) {
+ store.set(initialValue);
+ }
- if ((storage as SelfUpdateStorageInterface).addListener) {
- (storage as SelfUpdateStorageInterface).addListener(key, (newValue) => {
- store.set(newValue);
- });
- }
+ if ((storage as SelfUpdateStorageInterface).addListener) {
+ (storage as SelfUpdateStorageInterface).addListener(key, (newValue) => {
+ store.set(newValue);
+ });
+ }
- store.subscribe((value) => {
- storage.setValue(key, value);
- });
+ store.subscribe((value) => {
+ storage.setValue(key, value);
+ });
- return {
- ...store,
- delete() {
- storage.deleteValue(key);
- }
- };
+ return {
+ ...store,
+ delete() {
+ storage.deleteValue(key);
+ }
+ };
}
function getBrowserStorage(
- browserStorage: Storage,
- listenExternalChanges = false
+ browserStorage: Storage,
+ listenExternalChanges = false
): SelfUpdateStorageInterface {
- const listeners: Array<{ key: string; listener: (newValue: any) => void }> = [];
- const listenerFunction = (event: StorageEvent) => {
- const eventKey = event.key;
- if (event.storageArea === browserStorage) {
- listeners
- .filter(({ key }) => key === eventKey)
- .forEach(({ listener }) => {
- listener(deserialize(event.newValue));
- });
- }
- };
- const connect = () => {
- if (listenExternalChanges && typeof window !== 'undefined' && window?.addEventListener) {
- window.addEventListener('storage', listenerFunction);
- }
- };
- const disconnect = () => {
- if (listenExternalChanges && typeof window !== 'undefined' && window?.removeEventListener) {
- window.removeEventListener('storage', listenerFunction);
- }
- };
+ const listeners: Array<{ key: string; listener: (newValue: any) => void }> = [];
+ const listenerFunction = (event: StorageEvent) => {
+ const eventKey = event.key;
+ if (event.storageArea === browserStorage) {
+ listeners
+ .filter(({ key }) => key === eventKey)
+ .forEach(({ listener }) => {
+ listener(deserialize(event.newValue));
+ });
+ }
+ };
+ const connect = () => {
+ if (listenExternalChanges && typeof window !== 'undefined' && window?.addEventListener) {
+ window.addEventListener('storage', listenerFunction);
+ }
+ };
+ const disconnect = () => {
+ if (listenExternalChanges && typeof window !== 'undefined' && window?.removeEventListener) {
+ window.removeEventListener('storage', listenerFunction);
+ }
+ };
- return {
- addListener(key: string, listener: (newValue: any) => void) {
- listeners.push({ key, listener });
- if (listeners.length === 1) {
- connect();
- }
- },
- removeListener(key: string, listener: (newValue: any) => void) {
- const index = listeners.indexOf({ key, listener });
- if (index !== -1) {
- listeners.splice(index, 1);
- }
- if (listeners.length === 0) {
- disconnect();
- }
- },
- getValue(key: string): any | null {
- const value = browserStorage.getItem(key);
- return deserialize(value);
- },
- deleteValue(key: string) {
- browserStorage.removeItem(key);
- },
- setValue(key: string, value: any) {
- browserStorage.setItem(key, serialize(value));
- }
- };
+ return {
+ addListener(key: string, listener: (newValue: any) => void) {
+ listeners.push({ key, listener });
+ if (listeners.length === 1) {
+ connect();
+ }
+ },
+ removeListener(key: string, listener: (newValue: any) => void) {
+ const index = listeners.indexOf({ key, listener });
+ if (index !== -1) {
+ listeners.splice(index, 1);
+ }
+ if (listeners.length === 0) {
+ disconnect();
+ }
+ },
+ getValue(key: string): any | null {
+ const value = browserStorage.getItem(key);
+ return deserialize(value);
+ },
+ deleteValue(key: string) {
+ browserStorage.removeItem(key);
+ },
+ setValue(key: string, value: any) {
+ browserStorage.setItem(key, serialize(value));
+ }
+ };
}
/**
@@ -240,26 +240,26 @@ function getBrowserStorage(
* @param {boolean} listenExternalChanges - Update the store if the localStorage is updated from another page
*/
export function localStorage(listenExternalChanges = false): StorageInterface {
- if (typeof window !== 'undefined' && window?.localStorage) {
- return getBrowserStorage(window.localStorage, listenExternalChanges);
- }
- warnStorageNotFound('window.localStorage');
- return noopStorage();
+ if (typeof window !== 'undefined' && window?.localStorage) {
+ return getBrowserStorage(window.localStorage, listenExternalChanges);
+ }
+ warnStorageNotFound('window.localStorage');
+ return noopStorage();
}
/**
* Storage implementation that do nothing
*/
export function noopStorage(): StorageInterface {
- return {
- getValue(): null {
- return null;
- },
- deleteValue() {
- // Do nothing
- },
- setValue() {
- // Do nothing
- }
- };
+ return {
+ getValue(): null {
+ return null;
+ },
+ deleteValue() {
+ // Do nothing
+ },
+ setValue() {
+ // Do nothing
+ }
+ };
}
diff --git a/src/lib/util/serde.test.ts b/src/lib/util/serde.test.ts
index b219c64c..c66f7f0f 100644
--- a/src/lib/util/serde.test.ts
+++ b/src/lib/util/serde.test.ts
@@ -4,37 +4,37 @@ import { defaultState } from './state';
import type { State } from '$lib/types';
describe('Serde tests', () => {
- const verifySerde = (state: State, serde?: SerdeType): string => {
- const serialized = serializeState(state, serde);
- const deserialized = deserializeState(serialized);
- expect(deserialized).to.deep.equal(state);
- return serialized;
- };
+ const verifySerde = (state: State, serde?: SerdeType): string => {
+ const serialized = serializeState(state, serde);
+ const deserialized = deserializeState(serialized);
+ expect(deserialized).to.deep.equal(state);
+ return serialized;
+ };
- it('should serialize and deserialize with default serde', () => {
- expect(verifySerde(defaultState)).toMatchInlineSnapshot(
- '"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
- );
- });
+ it('should serialize and deserialize with default serde', () => {
+ expect(verifySerde(defaultState)).toMatchInlineSnapshot(
+ '"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
+ );
+ });
- it('should serialize and deserialize with base64 serde', () => {
- expect(verifySerde(defaultState, 'base64')).toMatchInlineSnapshot(
- '"base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cbiAgIiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjp0cnVlfQ"'
- );
- });
+ it('should serialize and deserialize with base64 serde', () => {
+ expect(verifySerde(defaultState, 'base64')).toMatchInlineSnapshot(
+ '"base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cbiAgIiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjp0cnVlfQ"'
+ );
+ });
- it('should serialize and deserialize with pako serde', () => {
- expect(verifySerde(defaultState, 'pako')).toMatchInlineSnapshot(
- '"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
- );
- });
+ it('should serialize and deserialize with pako serde', () => {
+ expect(verifySerde(defaultState, 'pako')).toMatchInlineSnapshot(
+ '"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
+ );
+ });
- it('should throw error for unrecognized serde', () => {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-expect-error
- expect(() => serializeState(defaultState, 'unknown')).toThrowError(
- 'Unknown serde type: unknown'
- );
- expect(() => deserializeState('unknown:hello')).toThrowError('Unknown serde type: unknown');
- });
+ it('should throw error for unrecognized serde', () => {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ expect(() => serializeState(defaultState, 'unknown')).toThrowError(
+ 'Unknown serde type: unknown'
+ );
+ expect(() => deserializeState('unknown:hello')).toThrowError('Unknown serde type: unknown');
+ });
});
diff --git a/src/lib/util/serde.ts b/src/lib/util/serde.ts
index 643401ff..3d2df939 100644
--- a/src/lib/util/serde.ts
+++ b/src/lib/util/serde.ts
@@ -3,61 +3,61 @@ import { toUint8Array, fromUint8Array, toBase64, fromBase64 } from 'js-base64';
import type { State } from '$lib/types';
interface Serde {
- serialize: (state: string) => string;
- deserialize: (state: string) => string;
+ 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);
- }
+ 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' });
- }
+ 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' });
+ }
};
export type SerdeType = 'base64' | 'pako';
const serdes: { [key in SerdeType]: Serde } = {
- base64: base64Serde,
- pako: pakoSerde
+ base64: base64Serde,
+ pako: pakoSerde
};
export const serializeState = (state: State, serde: SerdeType = 'pako'): string => {
- if (serdes[serde] === undefined) {
- throw new Error(`Unknown serde type: ${serde}`);
- }
- const json = JSON.stringify(state);
- const serialized = serdes[serde].serialize(json);
- return `${serde}:${serialized}`;
+ if (serdes[serde] === undefined) {
+ throw new Error(`Unknown serde type: ${serde}`);
+ }
+ const json = JSON.stringify(state);
+ const serialized = serdes[serde].serialize(json);
+ return `${serde}:${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 as SerdeType;
- } 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;
+ let type: SerdeType, serialized: string;
+ if (state.includes(':')) {
+ let tempType: string;
+ [tempType, serialized] = state.split(':');
+ if (tempType in serdes) {
+ type = tempType as SerdeType;
+ } 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;
};
diff --git a/src/lib/util/state.ts b/src/lib/util/state.ts
index 75728ac9..39337655 100644
--- a/src/lib/util/state.ts
+++ b/src/lib/util/state.ts
@@ -7,22 +7,22 @@ import { parse } from './mermaid';
import type { MarkerData, State, ValidatedState } from '$lib/types';
export const defaultState: State = {
- code: `graph TD
+ code: `graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
- mermaid: JSON.stringify(
- {
- theme: 'default'
- },
- null,
- 2
- ),
- autoSync: true,
- updateDiagram: true
+ mermaid: JSON.stringify(
+ {
+ theme: 'default'
+ },
+ null,
+ 2
+ ),
+ autoSync: true,
+ updateDiagram: true
};
const urlParseFailedState = `graph TD
@@ -40,165 +40,165 @@ const urlParseFailedState = `graph TD
export const inputStateStore = persist(writable(defaultState), localStorage(), 'codeStore');
export const currentState: ValidatedState = (() => {
- const state = get(inputStateStore);
- return {
- ...state,
- serialized: serializeState(state),
- errorMarkers: [],
- error: undefined,
- editorMode: state.editorMode ?? 'code'
- };
+ const state = get(inputStateStore);
+ return {
+ ...state,
+ serialized: serializeState(state),
+ errorMarkers: [],
+ error: undefined,
+ editorMode: state.editorMode ?? 'code'
+ };
})();
let q: AsyncQueue;
const processState = async (state: State) => {
- const processed: ValidatedState = {
- ...state,
- serialized: '',
- errorMarkers: [],
- error: undefined,
- editorMode: state.editorMode ?? 'code'
- };
- // No changes should be done to fields part of `state`.
- try {
- processed.serialized = serializeState(state);
- await parse(state.code);
- JSON.parse(state.mermaid);
- } catch (e) {
- processed.error = e;
- errorDebug();
- console.error(e);
- if (e.hash) {
- try {
- const marker: MarkerData = {
- severity: 8, // Error
- 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,
- message: e.str
- };
- processed.errorMarkers = [marker];
- } catch (err) {
- console.error('Error without line helper', err);
- }
- }
- }
- return processed;
+ const processed: ValidatedState = {
+ ...state,
+ serialized: '',
+ errorMarkers: [],
+ error: undefined,
+ editorMode: state.editorMode ?? 'code'
+ };
+ // No changes should be done to fields part of `state`.
+ try {
+ processed.serialized = serializeState(state);
+ await parse(state.code);
+ JSON.parse(state.mermaid);
+ } catch (e) {
+ processed.error = e;
+ errorDebug();
+ console.error(e);
+ if (e.hash) {
+ try {
+ const marker: MarkerData = {
+ severity: 8, // Error
+ 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,
+ message: e.str
+ };
+ processed.errorMarkers = [marker];
+ } catch (err) {
+ console.error('Error without line helper', err);
+ }
+ }
+ }
+ return processed;
};
// All internal reads should be done via stateStore, but it should not be persisted/shared externally.
export const stateStore: Readable = derived(
- [inputStateStore],
- ([state], set) => {
- if (!q) {
- // Initialize the queue for first time.
- q = new AsyncQueue(async (state: State) => {
- const newState = await processState(state);
- set(newState);
- });
- }
- q.process(state);
- },
- currentState
+ [inputStateStore],
+ ([state], set) => {
+ if (!q) {
+ // Initialize the queue for first time.
+ q = new AsyncQueue(async (state: State) => {
+ const newState = await processState(state);
+ set(newState);
+ });
+ }
+ q.process(state);
+ },
+ currentState
);
export const loadState = (data: string): void => {
- let state: State;
- console.log(`Loading '${data}'`);
- try {
- state = deserializeState(data);
- const mermaidConfig: { [key: string]: string } =
- typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
- if (
- mermaidConfig.securityLevel &&
- mermaidConfig.securityLevel !== 'strict' &&
- confirm(
- `Removing "securityLevel":"${mermaidConfig.securityLevel}" from the config for safety.\nClick Cancel if you trust the source of this Diagram.`
- )
- ) {
- delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
- }
- state.mermaid = JSON.stringify(mermaidConfig, null, 2);
- } catch (e) {
- state = get(inputStateStore);
- if (data) {
- console.error('Init error', e);
- state.code = urlParseFailedState;
- state.mermaid = defaultState.mermaid;
- }
- }
- updateCodeStore(state);
+ let state: State;
+ console.log(`Loading '${data}'`);
+ try {
+ state = deserializeState(data);
+ const mermaidConfig: { [key: string]: string } =
+ typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
+ if (
+ mermaidConfig.securityLevel &&
+ mermaidConfig.securityLevel !== 'strict' &&
+ confirm(
+ `Removing "securityLevel":"${mermaidConfig.securityLevel}" from the config for safety.\nClick Cancel if you trust the source of this Diagram.`
+ )
+ ) {
+ delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
+ }
+ state.mermaid = JSON.stringify(mermaidConfig, null, 2);
+ } catch (e) {
+ state = get(inputStateStore);
+ if (data) {
+ console.error('Init error', e);
+ state.code = urlParseFailedState;
+ state.mermaid = defaultState.mermaid;
+ }
+ }
+ updateCodeStore(state);
};
export const updateCodeStore = (newState: Partial): void => {
- inputStateStore.update((state) => {
- return { ...state, ...newState };
- });
+ inputStateStore.update((state) => {
+ return { ...state, ...newState };
+ });
};
let prompted = false;
export const updateCode = (
- code: string,
- {
- updateDiagram = false,
- resetPanZoom = false
- }: { updateDiagram?: boolean; resetPanZoom?: boolean } = {}
+ code: string,
+ {
+ updateDiagram = false,
+ resetPanZoom = false
+ }: { updateDiagram?: boolean; resetPanZoom?: boolean } = {}
): void => {
- console.log('updateCode', code);
- const lines = countLines(code);
- saveStatistics(code);
- errorDebug();
- if (lines > 50 && !prompted && get(stateStore).autoSync) {
- const turnOff = confirm(
- `Long diagram detected. Turn off Auto Sync? Use ${cmdKey} + Enter or click the sync logo to manually sync.`
- );
- prompted = true;
- if (turnOff) {
- updateCodeStore({
- autoSync: false
- });
- }
- }
+ console.log('updateCode', code);
+ const lines = countLines(code);
+ saveStatistics(code);
+ errorDebug();
+ if (lines > 50 && !prompted && get(stateStore).autoSync) {
+ const turnOff = confirm(
+ `Long diagram detected. Turn off Auto Sync? Use ${cmdKey} + Enter or click the sync logo to manually sync.`
+ );
+ prompted = true;
+ if (turnOff) {
+ updateCodeStore({
+ autoSync: false
+ });
+ }
+ }
- inputStateStore.update((state) => {
- if (resetPanZoom) {
- state.pan = undefined;
- state.zoom = undefined;
- }
- return { ...state, code, updateDiagram };
- });
+ inputStateStore.update((state) => {
+ if (resetPanZoom) {
+ state.pan = undefined;
+ state.zoom = undefined;
+ }
+ return { ...state, code, updateDiagram };
+ });
};
export const updateConfig = (config: string): void => {
- console.log('updateConfig', config);
- inputStateStore.update((state) => {
- return { ...state, mermaid: config };
- });
+ console.log('updateConfig', config);
+ inputStateStore.update((state) => {
+ return { ...state, mermaid: config };
+ });
};
export const toggleDarkTheme = (dark: boolean): void => {
- inputStateStore.update((state) => {
- const config = JSON.parse(state.mermaid);
- if (!config.theme || ['dark', 'default'].includes(config.theme)) {
- config.theme = dark ? 'dark' : 'default';
- }
+ inputStateStore.update((state) => {
+ const config = JSON.parse(state.mermaid);
+ if (!config.theme || ['dark', 'default'].includes(config.theme)) {
+ config.theme = dark ? 'dark' : 'default';
+ }
- return { ...state, mermaid: JSON.stringify(config, null, 2) };
- });
+ return { ...state, mermaid: JSON.stringify(config, null, 2) };
+ });
};
let urlDebounce: number;
export const initURLSubscription = (): void => {
- stateStore.subscribe(({ serialized }) => {
- clearTimeout(urlDebounce);
- urlDebounce = window.setTimeout(() => {
- history.replaceState(undefined, undefined, `#${serialized}`);
- }, 250);
- });
+ stateStore.subscribe(({ serialized }) => {
+ clearTimeout(urlDebounce);
+ urlDebounce = window.setTimeout(() => {
+ history.replaceState(undefined, undefined, `#${serialized}`);
+ }, 250);
+ });
};
export const getStateString = (): string => {
- return JSON.stringify(get(inputStateStore));
+ return JSON.stringify(get(inputStateStore));
};
diff --git a/src/lib/util/stats.test.ts b/src/lib/util/stats.test.ts
index 09db84f5..518a55b1 100644
--- a/src/lib/util/stats.test.ts
+++ b/src/lib/util/stats.test.ts
@@ -1,19 +1,19 @@
import { describe, it, expect } from 'vitest';
import { detectType } from './stats';
describe('diagram detection', () => {
- it('should detect diagrams correctly', () => {
- expect(
- detectType(`%%{{
+ it('should detect diagrams correctly', () => {
+ expect(
+ detectType(`%%{{
graph`)
- ).toBe('graph');
- expect(detectType(`gitGraph`)).toBe('gitGraph');
- expect(
- detectType(`%%{{
+ ).toBe('graph');
+ expect(detectType(`gitGraph`)).toBe('gitGraph');
+ expect(
+ detectType(`%%{{
flowChart
graph`)
- ).toBe('flowChart');
- expect(detectType(`loki -> thor`)).toBe(undefined);
- });
+ ).toBe('flowChart');
+ expect(detectType(`loki -> thor`)).toBe(undefined);
+ });
});
diff --git a/src/lib/util/stats.ts b/src/lib/util/stats.ts
index 9b4b9209..b86405ce 100644
--- a/src/lib/util/stats.ts
+++ b/src/lib/util/stats.ts
@@ -3,96 +3,96 @@ import type { AnalyticsInstance } from 'analytics';
export let analytics: AnalyticsInstance;
export const initAnalytics = async (): Promise => {
- if (browser && !analytics) {
- try {
- const [{ Analytics }, { default: plausible }] = await Promise.all([
- import('analytics'),
- import('analytics-plugin-plausible')
- ]);
- analytics = Analytics({
- app: 'mermaid-live-editor',
- plugins: [
- plausible({
- domain: 'mermaid.live',
- hashMode: false,
- trackLocalhost: false, // By default 'false'
- apiHost: 'https://plausible.io'
- })
- ]
- });
- } catch (e) {
- console.log(e);
- console.info('Analytics blocked ;)');
- }
- }
+ if (browser && !analytics) {
+ try {
+ const [{ Analytics }, { default: plausible }] = await Promise.all([
+ import('analytics'),
+ import('analytics-plugin-plausible')
+ ]);
+ analytics = Analytics({
+ app: 'mermaid-live-editor',
+ plugins: [
+ plausible({
+ domain: 'mermaid.live',
+ hashMode: false,
+ trackLocalhost: false, // By default 'false'
+ apiHost: 'https://plausible.io'
+ })
+ ]
+ });
+ } catch (e) {
+ console.log(e);
+ console.info('Analytics blocked ;)');
+ }
+ }
};
export const detectType = (text: string): string => {
- const possibleDiagramTypes = [
- 'classDiagram',
- 'erDiagram',
- 'flowChart',
- 'gantt',
- 'gitGraph',
- 'graph',
- 'journey',
- 'pie',
- 'stateDiagram'
- ];
- const firstLine = text
- .replace(/^\s*%%.*\n/g, '\n')
- .trimStart()
- .split(' ')[0]
- .toLowerCase();
- const detectedDiagram = possibleDiagramTypes.find((d) => firstLine.includes(d.toLowerCase()));
- return detectedDiagram;
+ const possibleDiagramTypes = [
+ 'classDiagram',
+ 'erDiagram',
+ 'flowChart',
+ 'gantt',
+ 'gitGraph',
+ 'graph',
+ 'journey',
+ 'pie',
+ 'stateDiagram'
+ ];
+ const firstLine = text
+ .replace(/^\s*%%.*\n/g, '\n')
+ .trimStart()
+ .split(' ')[0]
+ .toLowerCase();
+ const detectedDiagram = possibleDiagramTypes.find((d) => firstLine.includes(d.toLowerCase()));
+ return detectedDiagram;
};
export const countLines = (code: string): number => {
- return (code.match(/\n/g) || '').length + 1;
+ return (code.match(/\n/g) || '').length + 1;
};
export const saveStatistics = (graph: string): void => {
- const graphType = detectType(graph);
- if (!graphType) {
- return;
- }
- const length = countLines(graph);
- logEvent('render', { graphType, length });
+ const graphType = detectType(graph);
+ if (!graphType) {
+ return;
+ }
+ const length = countLines(graph);
+ logEvent('render', { graphType, length });
};
const minutesToMilliSeconds = (minutes: number): number => {
- return minutes * 60_000;
+ return minutes * 60_000;
};
const defaultDelay = minutesToMilliSeconds(1);
const delaysPerEvent = {
- render: minutesToMilliSeconds(5),
- panZoom: minutesToMilliSeconds(10),
- copyClipboard: defaultDelay,
- download: defaultDelay,
- copyMarkdown: defaultDelay,
- loadGist: defaultDelay,
- loadSampleDiagram: defaultDelay,
- renderDiagram: defaultDelay,
- history: defaultDelay,
- migration: defaultDelay,
- themeChange: defaultDelay
+ render: minutesToMilliSeconds(5),
+ panZoom: minutesToMilliSeconds(10),
+ copyClipboard: defaultDelay,
+ download: defaultDelay,
+ copyMarkdown: defaultDelay,
+ loadGist: defaultDelay,
+ loadSampleDiagram: defaultDelay,
+ renderDiagram: defaultDelay,
+ history: defaultDelay,
+ migration: defaultDelay,
+ themeChange: defaultDelay
};
export type AnalyticsEvent = keyof typeof delaysPerEvent;
const timeouts: Record = {};
// manual debounce to reduce the number of events sent to analytics
export const logEvent = (name: AnalyticsEvent, data?: unknown): void => {
- if (!analytics) {
- return;
- }
- const key = data ? JSON.stringify({ name, data }) : name;
- if (timeouts[key] === undefined) {
- void analytics.track(name, data);
- } else {
- clearTimeout(timeouts[key]);
- }
- timeouts[key] = window.setTimeout(() => {
- delete timeouts[key];
- }, delaysPerEvent[name]);
+ if (!analytics) {
+ return;
+ }
+ const key = data ? JSON.stringify({ name, data }) : name;
+ if (timeouts[key] === undefined) {
+ void analytics.track(name, data);
+ } else {
+ clearTimeout(timeouts[key]);
+ }
+ timeouts[key] = window.setTimeout(() => {
+ delete timeouts[key];
+ }, delaysPerEvent[name]);
};
diff --git a/src/lib/util/theme.ts b/src/lib/util/theme.ts
index 4221ee06..0da7f3ad 100644
--- a/src/lib/util/theme.ts
+++ b/src/lib/util/theme.ts
@@ -4,35 +4,35 @@ import { persist, localStorage } from '$lib/util/persist';
import { logEvent } from './stats';
export interface ThemeConfig {
- isDark: boolean;
- theme?: string;
+ isDark: boolean;
+ theme?: string;
}
export const themeStore: Writable = persist(
- writable({
- isDark: false
- }),
- localStorage(),
- 'themeStore'
+ writable({
+ isDark: false
+ }),
+ localStorage(),
+ 'themeStore'
);
const darkThemes = [
- 'dark',
- 'synthwave',
- 'halloween',
- 'aqua',
- 'forest',
- 'luxury',
- 'black',
- 'dracula'
+ 'dark',
+ 'synthwave',
+ 'halloween',
+ 'aqua',
+ 'forest',
+ 'luxury',
+ 'black',
+ 'dracula'
];
export const setTheme = (theme: string): void => {
- if (theme.includes(' ')) {
- theme = theme.split(' ')[1].trim();
- }
- const isDark = darkThemes.includes(theme);
- console.log('Setting theme', theme);
- themeStore.set({ theme, isDark });
- logEvent('themeChange', { theme, isDark });
+ if (theme.includes(' ')) {
+ theme = theme.split(' ')[1].trim();
+ }
+ const isDark = darkThemes.includes(theme);
+ console.log('Setting theme', theme);
+ themeStore.set({ theme, isDark });
+ logEvent('themeChange', { theme, isDark });
};
diff --git a/src/lib/util/util.ts b/src/lib/util/util.ts
index 96f72889..7eeb64c9 100644
--- a/src/lib/util/util.ts
+++ b/src/lib/util/util.ts
@@ -6,24 +6,24 @@ import { applyMigrations } from './migrations';
import { init } from './mermaid';
export const loadStateFromURL = (): void => {
- loadState(window.location.hash.slice(1));
+ loadState(window.location.hash.slice(1));
};
export const syncDiagram = (): void => {
- updateCodeStore({
- updateDiagram: true
- });
+ updateCodeStore({
+ updateDiagram: true
+ });
};
export const initHandler = async (): Promise => {
- applyMigrations();
- await initLoading('Loading Mermaid...', init());
- loadStateFromURL();
- await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
- syncDiagram();
- initURLSubscription();
- await initAnalytics();
- analytics?.page();
+ applyMigrations();
+ await initLoading('Loading Mermaid...', init());
+ loadStateFromURL();
+ await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
+ syncDiagram();
+ initURLSubscription();
+ await initAnalytics();
+ analytics?.page();
};
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
@@ -31,31 +31,31 @@ export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
let count = 0;
export const errorDebug = (limit = 100) => {
- count += 1;
- if (count > limit) {
- console.log(count, limit);
- // eslint-disable-next-line no-debugger
- debugger;
- }
+ count += 1;
+ if (count > limit) {
+ console.log(count, limit);
+ // eslint-disable-next-line no-debugger
+ debugger;
+ }
};
// To queue async tasks so they are executed one after the other, not concurrently.
export class AsyncQueue {
- private readonly queue: T[] = [];
- private running = false;
- constructor(private processor: (item: T) => Promise) {}
- public async process(item: T): Promise {
- this.queue.push(item);
- if (this.running) {
- return;
- }
- this.running = true;
- while (this.queue.length > 0) {
- const item = this.queue.shift();
- if (item) {
- await this.processor(item);
- }
- }
- this.running = false;
- }
+ private readonly queue: T[] = [];
+ private running = false;
+ constructor(private processor: (item: T) => Promise) {}
+ public async process(item: T): Promise {
+ this.queue.push(item);
+ if (this.running) {
+ return;
+ }
+ this.running = true;
+ while (this.queue.length > 0) {
+ const item = this.queue.shift();
+ if (item) {
+ await this.processor(item);
+ }
+ }
+ this.running = false;
+ }
}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 26aa808c..4c14fe93 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,87 +1,87 @@
-
+
{#if $loadingStateStore.loading}
-
-
-
-
{$loadingStateStore.message}
-
-
+
+
+
+
{$loadingStateStore.message}
+
+
{/if}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index ca9daa13..4a12a8b8 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,17 +1,17 @@
diff --git a/src/routes/edit/+page.svelte b/src/routes/edit/+page.svelte
index 19293e86..e3977afa 100644
--- a/src/routes/edit/+page.svelte
+++ b/src/routes/edit/+page.svelte
@@ -1,202 +1,202 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
- {#if !$stateStore.autoSync}
-
- {/if}
+ {#if !$stateStore.autoSync}
+
+ {/if}
-
-
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
- Code editing not supported on mobile. Please use a desktop browser.
-
-
-
+
+
+
+
+
+ Code editing not supported on mobile. Please use a desktop browser.
+
+
+
diff --git a/src/routes/view/+page.svelte b/src/routes/view/+page.svelte
index 216251f0..8de80023 100644
--- a/src/routes/view/+page.svelte
+++ b/src/routes/view/+page.svelte
@@ -1,8 +1,8 @@
diff --git a/src/tests/setup.ts b/src/tests/setup.ts
index f0c016c1..51d4037f 100644
--- a/src/tests/setup.ts
+++ b/src/tests/setup.ts
@@ -5,7 +5,7 @@ expect.extend(matchers);
// TODO: Remove once https://github.com/sveltejs/kit/issues/6259 is closed.
beforeAll(() => {
- vi.mock('$app/environment', () => ({
- browser: 'window' in globalThis
- }));
+ vi.mock('$app/environment', () => ({
+ browser: 'window' in globalThis
+ }));
});
diff --git a/svelte.config.js b/svelte.config.js
index c5c508a6..bbefdfde 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -3,24 +3,24 @@ import adapter from '@sveltejs/adapter-static';
/** @type {import('@sveltejs/kit').Config} */
const config = {
- // Consult https://github.com/sveltejs/svelte-preprocess
- // for more information about preprocessors
- preprocess: [
- preprocess({
- postcss: true
- })
- ],
- kit: {
- adapter: adapter({
- pages: `docs`
- }),
- paths: process.env['DEPLOY']
- ? {
- base: `/mermaid-live-editor`
- }
- : {},
- trailingSlash: 'ignore'
- }
+ // Consult https://github.com/sveltejs/svelte-preprocess
+ // for more information about preprocessors
+ preprocess: [
+ preprocess({
+ postcss: true
+ })
+ ],
+ kit: {
+ adapter: adapter({
+ pages: `docs`
+ }),
+ paths: process.env['DEPLOY']
+ ? {
+ base: `/mermaid-live-editor`
+ }
+ : {},
+ trailingSlash: 'ignore'
+ }
};
export default config;
diff --git a/tailwind.config.cjs b/tailwind.config.cjs
index dc72ba6e..18cb32c1 100644
--- a/tailwind.config.cjs
+++ b/tailwind.config.cjs
@@ -1,10 +1,10 @@
module.exports = {
- plugins: [require('daisyui')],
- content: ['./src/**/*.{html,js,svelte,ts}'],
- theme: {
- extend: {}
- },
- variants: {
- extend: {}
- }
+ plugins: [require('daisyui')],
+ content: ['./src/**/*.{html,js,svelte,ts}'],
+ theme: {
+ extend: {}
+ },
+ variants: {
+ extend: {}
+ }
};
diff --git a/tsconfig.json b/tsconfig.json
index 87bfd16b..38b29b7f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,17 +1,18 @@
{
- "include": [
- "src/**/*.d.ts",
- "src/**/*.ts",
- "src/**/*.svelte",
- "cypress/**/*.ts",
- "cypress/**/*.js",
- "static/**/*.js",
- "static/**/*.json"
- ],
- "compilerOptions": {
- "resolveJsonModule": true,
- "allowSyntheticDefaultImports": true,
- "types": ["vitest/importMeta"]
- },
- "extends": "./.svelte-kit/tsconfig.json"
+ "include": [
+ "src/**/*.d.ts",
+ "src/**/*.ts",
+ "src/**/*.svelte",
+ "cypress/**/*.ts",
+ "cypress/**/*.js",
+ "cypress/**/*.json",
+ "static/**/*.js",
+ "static/**/*.json"
+ ],
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "allowSyntheticDefaultImports": true,
+ "types": ["vitest/importMeta"]
+ },
+ "extends": "./.svelte-kit/tsconfig.json"
}
diff --git a/vite.config.js b/vite.config.js
index 4aee28c2..ecf6163b 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,26 +1,26 @@
import { sveltekit } from '@sveltejs/kit/vite';
/** @type {import('vite').UserConfig} */
const config = {
- plugins: [sveltekit()],
- envPrefix: 'MERMAID_',
- optimizeDeps: { include: ['mermaid'] },
- server: {
- port: 3000,
- host: true
- },
- preview: {
- port: 3000,
- host: true
- },
- test: {
- environment: 'jsdom',
- // in-source testing
- includeSource: ['src/**/*.{js,ts,svelte}'],
- setupFiles: ['./src/tests/setup.ts'],
- coverage: {
- exclude: ['src/mocks', '.svelte-kit', 'src/**/*.test.ts'],
- reporter: ['text', 'json', 'html', 'lcov']
- }
- }
+ plugins: [sveltekit()],
+ envPrefix: 'MERMAID_',
+ optimizeDeps: { include: ['mermaid'] },
+ server: {
+ port: 3000,
+ host: true
+ },
+ preview: {
+ port: 3000,
+ host: true
+ },
+ test: {
+ environment: 'jsdom',
+ // in-source testing
+ includeSource: ['src/**/*.{js,ts,svelte}'],
+ setupFiles: ['./src/tests/setup.ts'],
+ coverage: {
+ exclude: ['src/mocks', '.svelte-kit', 'src/**/*.test.ts'],
+ reporter: ['text', 'json', 'html', 'lcov']
+ }
+ }
};
export default config;