Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b606ef6f1f |
@@ -48,4 +48,3 @@ jobs:
|
||||
parallel: true
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
MERMAID_DISABLE_DEBOUNCE: 'true'
|
||||
|
||||
Vendored
+1
-2
@@ -3,6 +3,5 @@
|
||||
"cSpell.words": ["pako", "Serde", "serdes"],
|
||||
"vitest.commandLine": "yarn test:unit",
|
||||
"vitest.enable": true,
|
||||
"testing.autoRun.mode": "rerun",
|
||||
"svelte.enable-ts-plugin": true
|
||||
"testing.autoRun.mode": "rerun"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { verifyFileSize } from './util';
|
||||
import { disableDebounce, verifyFileSize } from './util';
|
||||
describe('Check actions', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/edit');
|
||||
disableDebounce();
|
||||
});
|
||||
|
||||
it('should update markdown code', () => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getEditor, cmd } from './util';
|
||||
import { getEditor, cmd, disableDebounce } from './util';
|
||||
|
||||
describe('Auto sync tests', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
disableDebounce();
|
||||
});
|
||||
|
||||
it('should dim diagram when code is edited', () => {
|
||||
@@ -71,6 +72,7 @@ describe.only('Pan and Zoom', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
disableDebounce();
|
||||
});
|
||||
it('should toggle pan and zoom', () => {
|
||||
cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { getEditor, verifyFileSnapshot } from './util';
|
||||
import { getEditor, disableDebounce, verifyFileSnapshot } from './util';
|
||||
|
||||
describe('Save History', () => {
|
||||
beforeEach(() => {
|
||||
cy.clock(new Date(2022, 0, 1).getTime());
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/edit');
|
||||
|
||||
disableDebounce();
|
||||
cy.contains('Actions').click();
|
||||
cy.contains('History').click();
|
||||
});
|
||||
@@ -17,11 +17,11 @@ describe('Save History', () => {
|
||||
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"}]'
|
||||
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"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}","updateEditor":true,"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"}]'
|
||||
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"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}","updateEditor":true,"autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"x749ffc6-21dd-418a-b984-7c1ffc3146b3","name":"needy-mosquito"}]'
|
||||
);
|
||||
cy.reload();
|
||||
cy.contains('Actions').click();
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { toBase64 } from 'js-base64';
|
||||
import { disableDebounce } from './util';
|
||||
|
||||
describe('Site Loads', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
disableDebounce();
|
||||
});
|
||||
it('Check Home page load', () => {
|
||||
cy.url().should('include', '/edit');
|
||||
@@ -58,7 +60,7 @@ describe('Site Loads', () => {
|
||||
// Disabled temporarily. Should be enabled after the issue is fixed in Mermaid.
|
||||
// it('should prevent setting the "securityLevel" option via URL', () => {
|
||||
// const b64State = toBase64(
|
||||
// `{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
|
||||
// `{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
||||
// true
|
||||
// );
|
||||
// cy.on('window:confirm', () => true);
|
||||
@@ -73,7 +75,7 @@ describe('Site Loads', () => {
|
||||
|
||||
it('should allow persisting "securityLevel" using confirm dialogue', () => {
|
||||
const b64State = toBase64(
|
||||
`{"code":"graph TD\\nA[\\"<img src='https://dummyimage.com/64' width=64/>\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
|
||||
`{"code":"graph TD\\nA[\\"<img src='https://dummyimage.com/64' width=64/>\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
||||
true
|
||||
);
|
||||
cy.on('window:confirm', () => false);
|
||||
|
||||
@@ -8,6 +8,8 @@ export const getEditor = ({ bottom = true, newline = false } = {}) =>
|
||||
.type(`${bottom ? '{pageDown}' : cmd}`)
|
||||
.type(`${newline ? '{enter}' : cmd}`);
|
||||
|
||||
export const disableDebounce = () => cy.setLocalStorage('noDebounce', 'true');
|
||||
|
||||
const downloadsFolder = Cypress.config('downloadsFolder');
|
||||
|
||||
export const verifyFileSize = (
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
module.exports = {
|
||||
"Site Loads": {
|
||||
"Check Home page load": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true}"
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":true,\"autoSync\":true,\"updateDiagram\":true}"
|
||||
},
|
||||
"Check Redirect from old URL": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true}"
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}"
|
||||
},
|
||||
"should load diagram from gist": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a\"}}}"
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a\"}}}"
|
||||
},
|
||||
"should load diagram from gist revision": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19\"}}}"
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19\"}}}"
|
||||
},
|
||||
"should load diagram from raw files": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
|
||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
|
||||
}
|
||||
},
|
||||
"__version": "10.6.0",
|
||||
"Auto sync tests": {
|
||||
"should dim diagram when code is edited": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":false,\"updateDiagram\":false}"
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}"
|
||||
},
|
||||
"should not dim diagram when code is in sync": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":false}"
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}"
|
||||
}
|
||||
},
|
||||
"Test themes": {
|
||||
|
||||
+1
-2
@@ -5,7 +5,6 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"dev:test": "MERMAID_DISABLE_DEBOUNCE=true yarn dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "prettier --check --cache --plugin-search-dir=. .;eslint --ignore-path .gitignore .",
|
||||
@@ -56,7 +55,7 @@
|
||||
"postcss-load-config": "4.0.1",
|
||||
"prettier": "2.7.1",
|
||||
"prettier-plugin-svelte": "2.7.0",
|
||||
"svelte": "3.49.0",
|
||||
"svelte": "3.50.0",
|
||||
"svelte-preprocess": "4.10.7",
|
||||
"tailwindcss": "3.1.8",
|
||||
"tslib": "2.4.0",
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
export let isCloseable = true;
|
||||
export let isOpen = true;
|
||||
export let tabs: Tab[] = [];
|
||||
export let activeTabID: string = '';
|
||||
export let title: string;
|
||||
$: isOpen = isCloseable ? isOpen : true;
|
||||
$: isTabsShown = isOpen && tabs.length > 0;
|
||||
@@ -16,7 +15,7 @@
|
||||
class="bg-primary p-2 {isTabsShown ? 'pb-0' : ''} flex-none cursor-pointer"
|
||||
on:click={() => (isOpen = !isOpen)}>
|
||||
<div class="flex justify-between">
|
||||
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} {activeTabID} />
|
||||
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} />
|
||||
<div class="flex gap-x-4 items-center {isTabsShown ? '-mt-2' : ''}">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
export let isCloseable = true;
|
||||
export let tabs: Tab[];
|
||||
export let tabs: Tab[] = [];
|
||||
export let title: string;
|
||||
export let isOpen = false;
|
||||
export let activeTabID: string;
|
||||
|
||||
if (!activeTabID && tabs.length > 0) {
|
||||
activeTabID = tabs[0].id;
|
||||
}
|
||||
$: activeTabID = tabs[0]?.id;
|
||||
|
||||
const dispatch = createEventDispatcher<TabEvents>();
|
||||
const toggleTabs = (tab: Tab) => {
|
||||
activeTabID = tab.id;
|
||||
|
||||
@@ -1,95 +1,77 @@
|
||||
<script lang="ts">
|
||||
import type { EditorMode } from '$lib/types';
|
||||
import { stateStore, updateCode, updateConfig } from '$lib/util/state';
|
||||
import type { EditorEvents } from '$lib/types';
|
||||
import { stateStore } from '$lib/util/state';
|
||||
import { themeStore } from '$lib/util/theme';
|
||||
import { syncDiagram } from '$lib/util/util';
|
||||
import type monaco from 'monaco-editor';
|
||||
import { onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import initEditor from 'monaco-mermaid';
|
||||
import { logEvent } from '$lib/util/stats';
|
||||
import { debounceEnabled } from '$lib/util/env';
|
||||
|
||||
let divEl: HTMLDivElement = null;
|
||||
let editor: monaco.editor.IStandaloneCodeEditor;
|
||||
let Monaco: typeof monaco;
|
||||
let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
let Monaco;
|
||||
|
||||
export let text: string;
|
||||
export let language: string;
|
||||
export let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
value: text,
|
||||
language: language,
|
||||
minimap: {
|
||||
enabled: false
|
||||
},
|
||||
theme: 'mermaid',
|
||||
overviewRulerLanes: 0
|
||||
};
|
||||
let text = '';
|
||||
let oldText = text;
|
||||
$: editor && Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
||||
|
||||
stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
|
||||
if (!editor) return;
|
||||
|
||||
// Update editor text if it's different
|
||||
const newText = editorMode === 'code' ? code : mermaid;
|
||||
if (newText !== text) {
|
||||
editor.setValue(newText);
|
||||
text = newText;
|
||||
const handleTextUpdate = (newText: string) => {
|
||||
if (newText !== oldText) {
|
||||
if ($stateStore.updateEditor) {
|
||||
editor?.setValue(newText);
|
||||
}
|
||||
oldText = newText;
|
||||
}
|
||||
};
|
||||
|
||||
// Update editor mode if it's different
|
||||
const language = editorMode === 'code' ? 'mermaid' : 'json';
|
||||
if (editor.getModel().getLanguageId() !== language) {
|
||||
Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
||||
}
|
||||
|
||||
// Display errors if present
|
||||
if (errorMarkers.length > 0) {
|
||||
Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
|
||||
}
|
||||
$: handleTextUpdate(text);
|
||||
stateStore.subscribe(({ errorMarkers }) => {
|
||||
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
|
||||
});
|
||||
|
||||
themeStore.subscribe(({ isDark }) => {
|
||||
editor && Monaco?.editor.setTheme(isDark ? 'mermaid-dark' : 'mermaid');
|
||||
});
|
||||
|
||||
const handleUpdate = (text: string, mode: EditorMode) => {
|
||||
if (mode === 'code') {
|
||||
updateCode(text);
|
||||
} else {
|
||||
updateConfig(text);
|
||||
}
|
||||
};
|
||||
|
||||
// Debounce state updates to avoid performance issues
|
||||
let debounce: { [key: string]: number } = {};
|
||||
const updateHandler = (newText: string) => {
|
||||
text = newText;
|
||||
const mode = $stateStore.editorMode;
|
||||
if (debounceEnabled) {
|
||||
clearTimeout(debounce[mode]);
|
||||
debounce[mode] = window.setTimeout(() => {
|
||||
handleUpdate(text, mode);
|
||||
}, 300);
|
||||
} else {
|
||||
handleUpdate(text, mode);
|
||||
}
|
||||
};
|
||||
|
||||
const dispatch = createEventDispatcher<EditorEvents>();
|
||||
const loadMonaco = async () => {
|
||||
let i = 0;
|
||||
while (i++ < 500) {
|
||||
while (i++ < 10) {
|
||||
try {
|
||||
// @ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
|
||||
Monaco = window.monaco;
|
||||
Monaco = monaco;
|
||||
return;
|
||||
} catch {
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
}
|
||||
alert('Loading Monaco Editor failed. Please try refreshing the page.');
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
|
||||
try {
|
||||
// @ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
|
||||
Monaco = monaco;
|
||||
} catch {
|
||||
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
|
||||
}
|
||||
initEditor(Monaco);
|
||||
editor = Monaco.editor.create(divEl, editorOptions);
|
||||
editor.onDidChangeModelContent(() => {
|
||||
updateHandler(editor.getValue());
|
||||
oldText = editor.getValue();
|
||||
dispatch('update', {
|
||||
text: oldText
|
||||
});
|
||||
});
|
||||
editor.addAction({
|
||||
id: 'mermaid-render-diagram',
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
};
|
||||
|
||||
const restoreHistoryItem = (state: State): void => {
|
||||
inputStateStore.set({ ...state, updateDiagram: true });
|
||||
inputStateStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
||||
};
|
||||
|
||||
const relativeTime = (time: number) => {
|
||||
|
||||
@@ -95,11 +95,11 @@ 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"}]'
|
||||
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"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}","updateEditor":true,"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"}]'
|
||||
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"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}","updateEditor":true,"autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
|
||||
);
|
||||
let manualHistoryStore: HistoryEntry[] = JSON.parse(
|
||||
window.localStorage.getItem('manualHistoryStore')
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
const loadSampleDiagram = (diagramType: string): void => {
|
||||
updateCode(samples[diagramType], {
|
||||
updateDiagram: true,
|
||||
updateEditor: true,
|
||||
resetPanZoom: true
|
||||
});
|
||||
void logEvent('loadSampleDiagram', { diagramType });
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
outOfSync = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('view fail', e);
|
||||
console.log('view fail', e);
|
||||
error = true;
|
||||
}
|
||||
});
|
||||
|
||||
Vendored
+8
-4
@@ -16,6 +16,13 @@ export interface MarkerData {
|
||||
endColumn: number;
|
||||
}
|
||||
|
||||
export interface EditorUpdateEvent {
|
||||
text: string;
|
||||
}
|
||||
export interface EditorEvents {
|
||||
update: EditorUpdateEvent;
|
||||
}
|
||||
|
||||
export interface TabEvents {
|
||||
select: Tab;
|
||||
}
|
||||
@@ -29,9 +36,9 @@ export interface Tab {
|
||||
export interface State {
|
||||
code: string;
|
||||
mermaid: string;
|
||||
updateEditor: boolean;
|
||||
updateDiagram: boolean;
|
||||
autoSync: boolean;
|
||||
editorMode?: EditorMode;
|
||||
panZoom?: boolean;
|
||||
pan?: { x: number; y: number };
|
||||
zoom?: number;
|
||||
@@ -39,7 +46,6 @@ export interface State {
|
||||
}
|
||||
|
||||
export interface ValidatedState extends State {
|
||||
editorMode: EditorMode;
|
||||
error: unknown;
|
||||
errorMarkers: MarkerData[];
|
||||
serialized: string;
|
||||
@@ -80,7 +86,5 @@ export interface DocConfig {
|
||||
};
|
||||
}
|
||||
|
||||
export type EditorMode = 'code' | 'config';
|
||||
|
||||
export type Loader = (url: string) => Promise<State>;
|
||||
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
||||
|
||||
@@ -2,4 +2,3 @@ export const rendererUrl: string =
|
||||
(import.meta.env.MERMAID_RENDERER_URL as string) ?? 'https://mermaid.ink';
|
||||
export const krokiRendererUrl: string =
|
||||
(import.meta.env.MERMAID_KROKI_RENDERER_URL as string) ?? 'https://kroki.io';
|
||||
export const debounceEnabled: boolean = import.meta.env.MERMAID_DISABLE_DEBOUNCE !== 'true';
|
||||
|
||||
@@ -54,6 +54,7 @@ export const loadDataFromUrl = async (): Promise<void> => {
|
||||
updateCodeStore({
|
||||
...state,
|
||||
autoSync: true,
|
||||
updateDiagram: true
|
||||
updateDiagram: true,
|
||||
updateEditor: true
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,11 +5,11 @@ 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"}]'
|
||||
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"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}","updateEditor":true,"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"}]'
|
||||
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"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}","updateEditor":true,"autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,19 +13,19 @@ describe('Serde tests', () => {
|
||||
|
||||
it('should serialize and deserialize with default serde', () => {
|
||||
expect(verifySerde(defaultState)).toMatchInlineSnapshot(
|
||||
'"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
|
||||
'"pako:eNpVkM1qw0AMhF9F6JRC_AI-FBo7ySXQQnPz5iC8cnZp9oe1TAm2373rmEKik5j5ZhAasQ2ascRromjgXCsPeT6ayiTbi6P-AkXxPh1ZwAXP9wl2m2OA3oQYrb--rfxugaAaTwvGIMb6n3m1qkf-0_MEdXOiKCFenp3zb5hg39gvk-tfHZM4pw5NR2VHRUsJKkoPBLfoODmyOp8-LopCMexYYZlXzR0NN1Go_JzRIWoS3msrIWGuuvW8RRokfN99i6Wkgf-h2lL-hFvF-Q9-YFyS"'
|
||||
);
|
||||
});
|
||||
|
||||
it('should serialize and deserialize with base64 serde', () => {
|
||||
expect(verifySerde(defaultState, 'base64')).toMatchInlineSnapshot(
|
||||
'"base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cbiAgIiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjp0cnVlfQ"'
|
||||
'"base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cbiAgIiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsInVwZGF0ZUVkaXRvciI6ZmFsc2UsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjp0cnVlfQ"'
|
||||
);
|
||||
});
|
||||
|
||||
it('should serialize and deserialize with pako serde', () => {
|
||||
expect(verifySerde(defaultState, 'pako')).toMatchInlineSnapshot(
|
||||
'"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
|
||||
'"pako:eNpVkM1qw0AMhF9F6JRC_AI-FBo7ySXQQnPz5iC8cnZp9oe1TAm2373rmEKik5j5ZhAasQ2ascRromjgXCsPeT6ayiTbi6P-AkXxPh1ZwAXP9wl2m2OA3oQYrb--rfxugaAaTwvGIMb6n3m1qkf-0_MEdXOiKCFenp3zb5hg39gvk-tfHZM4pw5NR2VHRUsJKkoPBLfoODmyOp8-LopCMexYYZlXzR0NN1Go_JzRIWoS3msrIWGuuvW8RRokfN99i6Wkgf-h2lL-hFvF-Q9-YFyS"'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+9
-10
@@ -23,6 +23,7 @@ export const defaultState: State = {
|
||||
null,
|
||||
2
|
||||
),
|
||||
updateEditor: false,
|
||||
autoSync: true,
|
||||
updateDiagram: true
|
||||
};
|
||||
@@ -47,10 +48,8 @@ export const stateStore: Readable<ValidatedState> = derived([inputStateStore], (
|
||||
...state,
|
||||
serialized: '',
|
||||
errorMarkers: [],
|
||||
error: undefined,
|
||||
editorMode: state.editorMode ?? 'code'
|
||||
error: undefined
|
||||
};
|
||||
|
||||
// No changes should be done to fields part of `state`.
|
||||
try {
|
||||
processed.serialized = serializeState(state);
|
||||
@@ -103,12 +102,11 @@ export const loadState = (data: string): void => {
|
||||
state.mermaid = defaultState.mermaid;
|
||||
}
|
||||
}
|
||||
updateCodeStore({ ...state });
|
||||
updateCodeStore({ ...state, updateEditor: true });
|
||||
};
|
||||
|
||||
export const updateCodeStore = (newState: Partial<State>): void => {
|
||||
inputStateStore.update((state) => {
|
||||
// console.log({ newState, state });
|
||||
return { ...state, ...newState };
|
||||
});
|
||||
};
|
||||
@@ -117,9 +115,10 @@ let prompted = false;
|
||||
export const updateCode = (
|
||||
code: string,
|
||||
{
|
||||
updateEditor,
|
||||
updateDiagram = false,
|
||||
resetPanZoom = false
|
||||
}: { updateDiagram?: boolean; resetPanZoom?: boolean } = {}
|
||||
}: { updateEditor: boolean; updateDiagram?: boolean; resetPanZoom?: boolean }
|
||||
): void => {
|
||||
saveStatistics(code);
|
||||
const lines = (code.match(/\n/g) || '').length + 1;
|
||||
@@ -141,13 +140,13 @@ export const updateCode = (
|
||||
state.pan = undefined;
|
||||
state.zoom = undefined;
|
||||
}
|
||||
return { ...state, code, updateDiagram };
|
||||
return { ...state, code, updateEditor, updateDiagram };
|
||||
});
|
||||
};
|
||||
|
||||
export const updateConfig = (config: string): void => {
|
||||
export const updateConfig = (config: string, updateEditor: boolean): void => {
|
||||
inputStateStore.update((state) => {
|
||||
return { ...state, mermaid: config };
|
||||
return { ...state, mermaid: config, updateEditor };
|
||||
});
|
||||
};
|
||||
|
||||
@@ -158,7 +157,7 @@ export const toggleDarkTheme = (dark: boolean): void => {
|
||||
config.theme = dark ? 'dark' : 'default';
|
||||
}
|
||||
|
||||
return { ...state, mermaid: JSON.stringify(config, null, 2) };
|
||||
return { ...state, mermaid: JSON.stringify(config, null, 2), updateEditor: true };
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { initURLSubscription, loadState, updateCodeStore } from './state';
|
||||
import { analytics, initAnalytics } from './stats';
|
||||
import { loadDataFromUrl } from './fileLoaders/loader';
|
||||
import { initLoading } from './loading';
|
||||
import { applyMigrations } from './migrations';
|
||||
|
||||
export const loadStateFromURL = (): void => {
|
||||
loadState(window.location.hash.slice(1));
|
||||
@@ -15,7 +14,6 @@ export const syncDiagram = (): void => {
|
||||
};
|
||||
|
||||
export const initHandler = async (): Promise<void> => {
|
||||
applyMigrations();
|
||||
loadStateFromURL();
|
||||
await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
|
||||
syncDiagram();
|
||||
@@ -26,3 +24,5 @@ export const initHandler = async (): Promise<void> => {
|
||||
|
||||
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||
export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
|
||||
|
||||
export const debounceEnabled = window.localStorage.getItem('noDebounce') !== 'true';
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
import { setTheme, themeStore } from '$lib/util/theme';
|
||||
import { toggleDarkTheme } from '$lib/util/state';
|
||||
import { initHandler } from '$lib/util/util';
|
||||
import { applyMigrations } from '$lib/util/migrations';
|
||||
|
||||
// This can be removed once https://github.com/sveltejs/kit/issues/1612 is fixed.
|
||||
// Then move it into src and vite will bundle it automatically.
|
||||
onMount(() => {
|
||||
applyMigrations();
|
||||
window.addEventListener('hashchange', async (ev) => {
|
||||
await initHandler();
|
||||
});
|
||||
|
||||
@@ -6,12 +6,20 @@
|
||||
import View from '$lib/components/view.svelte';
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
import History from '$lib/components/history/history.svelte';
|
||||
import { inputStateStore, stateStore, updateCodeStore } from '$lib/util/state';
|
||||
import { cmdKey, initHandler, syncDiagram } from '$lib/util/util';
|
||||
import { updateCode, updateConfig, inputStateStore, stateStore } from '$lib/util/state';
|
||||
import { cmdKey, debounceEnabled, initHandler, syncDiagram } from '$lib/util/util';
|
||||
import { onMount } from 'svelte';
|
||||
import type { Tab, DocConfig, EditorMode, ValidatedState } from '$lib/types';
|
||||
import type { EditorUpdateEvent, State, Tab, DocConfig } from '$lib/types';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
type Modes = 'code' | 'config';
|
||||
type Languages = 'mermaid' | 'json';
|
||||
|
||||
let selectedMode: Modes = 'code';
|
||||
const languageMap: { [key in Modes]: Languages } = {
|
||||
code: 'mermaid',
|
||||
config: 'json'
|
||||
};
|
||||
const docURLBase = 'https://mermaid-js.github.io/mermaid';
|
||||
const docMap: DocConfig = {
|
||||
graph: {
|
||||
@@ -52,23 +60,34 @@
|
||||
config: '/#/gitgraph?id=gitgraph-specific-configuration-options'
|
||||
}
|
||||
};
|
||||
let text = '';
|
||||
let docURL = docURLBase;
|
||||
let activeTabID = 'code';
|
||||
stateStore.subscribe(({ code, editorMode }: ValidatedState) => {
|
||||
activeTabID = editorMode;
|
||||
const codeTypeMatch = /([\S]+)[\s\n]/.exec(code);
|
||||
let language: Languages = 'mermaid';
|
||||
const handleModeUpdate = (mode: Modes) => {
|
||||
if (mode === 'code') {
|
||||
text = $stateStore.code;
|
||||
} else {
|
||||
text = $stateStore.mermaid;
|
||||
}
|
||||
};
|
||||
$: language = languageMap[selectedMode];
|
||||
$: handleModeUpdate(selectedMode);
|
||||
|
||||
stateStore.subscribe((state: State) => {
|
||||
if (state.updateEditor) {
|
||||
text = selectedMode === 'code' ? state.code : state.mermaid;
|
||||
}
|
||||
const codeTypeMatch = /([\S]+)[\s\n]/.exec(state.code);
|
||||
if (codeTypeMatch && codeTypeMatch.length > 1) {
|
||||
const docKey = codeTypeMatch[1];
|
||||
const docConfig = docMap[docKey] ?? { code: '' };
|
||||
docURL = docURLBase + (docConfig[editorMode] ?? docConfig.code ?? '');
|
||||
docURL = docURLBase + (docConfig[selectedMode] ?? docConfig.code ?? '');
|
||||
}
|
||||
});
|
||||
|
||||
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
||||
const editorMode: EditorMode = message.detail.id === 'code' ? 'code' : 'config';
|
||||
updateCodeStore({ editorMode });
|
||||
selectedMode = message.detail.id === 'code' ? 'code' : 'config';
|
||||
$inputStateStore.updateEditor = true;
|
||||
};
|
||||
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
id: 'code',
|
||||
@@ -82,6 +101,28 @@
|
||||
}
|
||||
];
|
||||
|
||||
const handleUpdate = (text: string) => {
|
||||
if (selectedMode === 'code') {
|
||||
updateCode(text, {
|
||||
updateEditor: false
|
||||
});
|
||||
} else {
|
||||
updateConfig(text, false);
|
||||
}
|
||||
};
|
||||
|
||||
let debounce: { [key: string]: number } = {};
|
||||
const updateHandler = ({ detail: { text } }: CustomEvent<EditorUpdateEvent>) => {
|
||||
if (debounceEnabled) {
|
||||
clearTimeout(debounce[selectedMode]);
|
||||
debounce[selectedMode] = window.setTimeout(() => {
|
||||
handleUpdate(text);
|
||||
}, 300);
|
||||
} else {
|
||||
handleUpdate(text);
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
await initHandler();
|
||||
const resizer = document.getElementById('resizeHandler');
|
||||
@@ -108,7 +149,7 @@
|
||||
<Navbar />
|
||||
<div class="flex-1 flex overflow-hidden">
|
||||
<div class="hidden md:flex flex-col" id="editorPane" style="width: 40%">
|
||||
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} {activeTabID} title="Mermaid">
|
||||
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} title="Mermaid">
|
||||
<div slot="actions" class="flex flex-row items-center">
|
||||
<div class="form-control flex-row items-center">
|
||||
<label class="cursor-pointer label" for="autoSync">
|
||||
@@ -134,7 +175,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Editor />
|
||||
<Editor on:update={updateHandler} {language} {text} />
|
||||
</Card>
|
||||
|
||||
<div class="-mt-2">
|
||||
|
||||
@@ -5014,10 +5014,10 @@ svelte-preprocess@4.10.7:
|
||||
sorcery "^0.10.0"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
svelte@3.49.0:
|
||||
version "3.49.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029"
|
||||
integrity sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==
|
||||
svelte@3.50.0:
|
||||
version "3.50.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.50.0.tgz#d11a7a6bd1e084ec051d55104a9af8bccf54461f"
|
||||
integrity sha512-zXeOUDS7+85i+RxLN+0iB6PMbGH7OhEgjETcD1fD8ZrhuhNFxYxYEHU41xuhkHIulJavcu3PKbPyuCrBxdxskQ==
|
||||
|
||||
svg-pan-zoom@^3.6.1:
|
||||
version "3.6.1"
|
||||
|
||||
Reference in New Issue
Block a user