asyncable

This commit is contained in:
Sidharth Vinod
2022-10-19 15:17:52 +05:30
parent f7b3df9b76
commit 384e53d02e
15 changed files with 452 additions and 222 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"editor.formatOnSave": true,
"cSpell.words": ["pako", "Serde", "serdes"],
"cSpell.words": ["asyncable", "pako", "Serde", "serdes"],
"vitest.commandLine": "yarn test:unit",
"vitest.enable": true,
"testing.autoRun.mode": "rerun",
+2 -2
View File
@@ -10,7 +10,7 @@ describe('Auto sync tests', () => {
cy.contains('Auto sync').click();
cy.get('#view').should('not.have.class', 'outOfSync');
cy.get('#view').should('not.contain.text', 'Diagram out of sync.');
getEditor().type(' C --> Test');
getEditor({ bottom: true, newline: true }).type(' C --> Test');
cy.get('#view').should('have.class', 'outOfSync');
cy.get('#view').should('contain.text', 'Diagram out of sync.');
cy.getLocalStorage('codeStore').snapshot();
@@ -71,7 +71,7 @@ describe('Auto sync tests', () => {
});
});
describe.only('Pan and Zoom', () => {
describe('Pan and Zoom', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
+5 -3
View File
@@ -20,12 +20,13 @@ describe('Site Loads', () => {
it('should load sample diagrams when clicked', () => {
cy.contains('Sample Diagrams').click();
cy.contains('Pie Chart').click();
cy.contains('Pie').click();
cy.contains('pie title Pets adopted by volunteers');
cy.contains('Class Diagram').click();
cy.contains('Class').click();
cy.contains('classDiagram');
});
describe.skip('github', () => {
it('should load diagram from gist', () => {
cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`);
cy.contains('History').click();
@@ -54,6 +55,7 @@ describe('Site Loads', () => {
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', () => {
@@ -71,7 +73,7 @@ describe('Site Loads', () => {
// cy.get('#view').contains(`src='https://via.placeholder.com/64'`);
// });
it('should allow persisting "securityLevel" using confirm dialogue', () => {
it.only('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}`,
true
+3 -2
View File
@@ -5,6 +5,7 @@
"license": "MIT",
"scripts": {
"dev": "vite dev",
"dev:force": "MERMAID_LOCAL=true yarn dev --force",
"dev:test": "yarn dev",
"build": "vite build",
"preview": "vite preview",
@@ -26,7 +27,6 @@
"@sveltejs/kit": "1.0.0-next.516",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/svelte": "3.2.2",
"@types/mermaid": "9.1.0",
"@types/pako": "2.0.0",
"@types/uuid": "8.3.4",
"@typescript-eslint/eslint-plugin": "5.40.1",
@@ -57,6 +57,7 @@
"prettier": "2.7.1",
"prettier-plugin-svelte": "2.8.0",
"svelte": "3.52.0",
"svelte-asyncable": "^2.1.0",
"svelte-preprocess": "4.10.7",
"tailwindcss": "3.1.8",
"tslib": "2.4.0",
@@ -69,7 +70,7 @@
"analytics-plugin-plausible": "0.0.6",
"daisyui": "2.31.0",
"js-base64": "3.7.2",
"mermaid": "9.1.7",
"mermaid": "9.2.0-rc7",
"moment": "2.29.4",
"monaco-editor": "0.34.1",
"monaco-mermaid": "1.0.6",
+14
View File
@@ -0,0 +1,14 @@
/// <reference types="vite/client" />
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...
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
+6 -3
View File
@@ -1,12 +1,13 @@
<script lang="ts">
import { browser } from '$app/environment';
import Card from '$lib/components/card/card.svelte';
import { krokiRendererUrl, rendererUrl } from '$lib/util/env';
import { env } from '$lib/util/env';
import { pakoSerde } from '$lib/util/serde';
import { stateStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import { toBase64 } from 'js-base64';
import moment from 'moment';
const { krokiRendererUrl, rendererUrl } = env;
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
@@ -139,7 +140,8 @@
};
let gistURL = '';
stateStore.subscribe(({ loader }) => {
stateStore.subscribe(async (state) => {
const { loader } = await state;
if (loader?.type === 'gist') {
// @ts-ignore Gist will have url
gistURL = loader.config.url;
@@ -165,7 +167,8 @@
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
isNetlify = true;
}
stateStore.subscribe(({ code, serialized }) => {
stateStore.subscribe(async (state) => {
const { code, serialized } = await state;
iUrl = `${rendererUrl}/img/${serialized}?type=png`;
svgUrl = `${rendererUrl}/svg/${serialized}`;
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize(code)}`;
+23 -10
View File
@@ -2,7 +2,7 @@
import type { EditorMode } from '$lib/types';
import { stateStore, updateCode, updateConfig } from '$lib/util/state';
import { themeStore } from '$lib/util/theme';
import { syncDiagram } from '$lib/util/util';
import { errorDebug, syncDiagram } from '$lib/util/util';
import type monaco from 'monaco-editor';
import { onMount } from 'svelte';
import initEditor from 'monaco-mermaid';
@@ -20,12 +20,15 @@
};
let text = '';
stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
stateStore.subscribe(async (state) => {
const { errorMarkers, editorMode, code, mermaid } = await state;
console.log('editor store subscription', { code, mermaid });
if (!editor) return;
// Update editor text if it's different
const newText = editorMode === 'code' ? code : mermaid;
if (newText !== text) {
console.log('updating editor text', newText);
editor.setValue(newText);
text = newText;
}
@@ -44,24 +47,25 @@
editor && Monaco?.editor.setTheme(isDark ? 'mermaid-dark' : 'mermaid');
});
const handleUpdate = (text: string, mode: EditorMode) => {
const handleUpdate = async (text: string, mode: EditorMode) => {
if (mode === 'code') {
updateCode(text);
await updateCode(text);
} else {
updateConfig(text);
}
};
const loadMonaco = async () => {
console.log('Loading Monaco...');
// errorDebug();
let i = 0;
while (i++ < 500) {
try {
// @ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
Monaco = window.monaco;
if (Monaco !== undefined) {
return;
} catch {
await new Promise((r) => setTimeout(r, 100));
}
await new Promise((r) => setTimeout(r, 100));
}
alert('Loading Monaco Editor failed. Please try refreshing the page.');
};
@@ -69,10 +73,17 @@
onMount(async () => {
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
initEditor(Monaco);
errorDebug(100);
editor = Monaco.editor.create(divEl, editorOptions);
editor.onDidChangeModelContent(() => {
text = editor.getValue();
handleUpdate(text, $stateStore.editorMode);
editor.onDidChangeModelContent(async () => {
const newText = editor.getValue();
console.log({ text, newText });
// errorDebug(500);
if (text === newText) {
return;
}
text = newText;
await handleUpdate(text, (await $stateStore).editorMode);
});
editor.addAction({
id: 'mermaid-render-diagram',
@@ -94,7 +105,9 @@
});
resizeObserver.observe(divEl.parentElement);
console.log(`editor mounted`);
return () => {
console.log(`editor disposed`);
editor.dispose();
};
});
+61 -31
View File
@@ -4,19 +4,18 @@
import { logEvent } from '$lib/util/stats';
const samples = {
'Flow Chart': `graph TD
Flow: `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]`,
'Sequence Diagram': `sequenceDiagram
Sequence: `sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!
`,
'Class Diagram': `classDiagram
John-->>-Alice: I feel great!`,
Class: `classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
@@ -36,17 +35,15 @@
class Zebra{
+bool is_wild
+run()
}
`,
'State Diagram': `stateDiagram-v2
}`,
State: `stateDiagram-v2
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]
`,
'Gantt Chart': `gantt
Crash --> [*]`,
Gantt: `gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
@@ -54,14 +51,12 @@
Another task :after a1 , 20d
section Another
Task in sec :2014-01-12 , 12d
another task : 24d
`,
'Pie Chart': `pie title Pets adopted by volunteers
another task : 24d`,
Pie: `pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
`,
'ER Diagram': `erDiagram
"Rats" : 15`,
ER: `erDiagram
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ INVOICE : "liable for"
@@ -69,9 +64,8 @@
INVOICE ||--|{ ORDER : covers
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
`,
'User Journey': ` journey
PRODUCT ||--o{ ORDER-ITEM : "ordered in"`,
'User Journey': `journey
title My working day
section Go to work
Make tea: 5: Me
@@ -79,9 +73,8 @@
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 3: Me
`,
'Git Graph': ` gitGraph
Sit down: 3: Me`,
Git: `gitGraph
commit
commit
branch develop
@@ -91,24 +84,61 @@
checkout main
merge develop
commit
commit
`
commit`,
Mindmap: `mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectivness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid`
};
const loadSampleDiagram = (diagramType: string): void => {
updateCode(samples[diagramType], {
const loadSampleDiagram = async (diagramType: string): Promise<void> => {
await updateCode(samples[diagramType], {
updateDiagram: true,
resetPanZoom: true
});
logEvent('loadSampleDiagram', { diagramType });
};
// Adding in this array will add an icon to the preset menu
const newDiagrams: Array<keyof typeof samples> = ['Mindmap'];
const diagramOrder: Array<keyof typeof samples> = [
'Sequence',
'Flow',
'Class',
'State',
'ER',
'Gantt',
'User Journey',
'Git',
'Pie',
'Mindmap'
];
</script>
<Card title="Sample Diagrams" isOpen={false}>
<div class="flex gap-2 flex-wrap p-2">
{#each Object.keys(samples) as sample}
<button class="btn btn-primary normal-case btn-sm" on:click={() => loadSampleDiagram(sample)}
>{sample}</button>
<div class="flex flex-wrap p-2 gap-2">
{#each diagramOrder as sample}
<button
class="btn btn-sm btn-primary w-28 normal-case flex-grow"
on:click={() => loadSampleDiagram(sample)}>
{sample}
{#if newDiagrams.includes(sample)}
<span class="ml-2 fa fa-heart" />
{/if}
</button>
{/each}
</div>
</Card>
+50 -13
View File
@@ -1,11 +1,11 @@
<script lang="ts">
import { inputStateStore, stateStore, updateCodeStore } from '$lib/util/state';
import { onMount } from 'svelte';
import mermaid from 'mermaid';
import panzoom from 'svg-pan-zoom';
import type { State, ValidatedState } from '$lib/types';
import { logEvent } from '$lib/util/stats';
import { cmdKey } from '$lib/util/util';
import { render as renderDiagram } from '$lib/util/mermaid';
let code = '';
let config = '';
@@ -15,7 +15,7 @@
let outOfSync = false;
let hide = false;
let manualUpdate = true;
let panZoomEnabled = $stateStore.panZoom;
let panZoomEnabled = false;
let pzoom: SvgPanZoom.Instance;
const handlePanZoomChange = () => {
@@ -50,9 +50,15 @@
});
};
const handleStateChange = (state: ValidatedState) => {
// let count = 0;
const handleStateChange = async (state: ValidatedState) => {
// const c = count;
// count += 1;
// console.log('handleStateChange', c, state);
panZoomEnabled;
if (state.error !== undefined) {
error = true;
// console.log('handleStateChange End error', c);
return;
}
error = false;
@@ -64,6 +70,7 @@
outOfSync = false;
manualUpdate = true;
if (code === state.code && config === state.mermaid && panZoomEnabled === state.panZoom) {
// console.log('handleStateChange End nochange', c);
// Do not render if there is no change in Code/Config/PanZoom
return;
}
@@ -72,16 +79,24 @@
panZoomEnabled = state.panZoom;
const scroll = view.parentElement.scrollTop;
delete container.dataset.processed;
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
mermaid.render('graph-div', code, (svgCode) => {
await renderDiagram(
Object.assign({}, JSON.parse(state.mermaid)),
code,
'graph-div',
(svgCode, bindFunctions) => {
if (svgCode.length > 0) {
handlePanZoom(state);
container.innerHTML = svgCode;
// console.log(container.innerHTML);
const graphDiv = document.getElementById('graph-div');
graphDiv.setAttribute('height', '100%');
graphDiv.style.maxWidth = '100%';
if (bindFunctions) {
bindFunctions(graphDiv);
}
});
}
}
);
view.parentElement.scrollTop = scroll;
error = false;
} else if (manualUpdate) {
@@ -93,22 +108,44 @@
console.error('view fail', e);
error = true;
}
// console.log('handleStateChange End', c);
};
const stateChanges = [];
let processing = false;
const processStateChanges = async () => {
if (processing) {
return;
}
processing = true;
while (stateChanges.length > 0) {
const state = stateChanges.shift();
await handleStateChange(state);
// if (stateChanges.length > 0) {
// Promise.resolve().then(processStateChanges);
// }
}
processing = false;
};
onMount(() => {
stateStore.subscribe((state) => {
handleStateChange(state);
stateStore.subscribe(async (state) => {
stateChanges.push(await state);
await processStateChanges();
});
window.addEventListener('resize', () => {
if ($stateStore.panZoom && pzoom) {
window.addEventListener('resize', async () => {
if ((await $stateStore).panZoom && pzoom) {
pzoom.resize();
}
});
console.log('View mounted');
});
</script>
{#if error && $stateStore.error instanceof Error}
<div class="p-2 text-red-600" id="errorContainer">{$stateStore.error}</div>
{/if}
{#await $stateStore then state}
{#if error && state.error instanceof Error}
<div class="p-2 text-red-600" id="errorContainer">{state.error}</div>
{/if}
{/await}
{#if outOfSync}
<div class="absolute w-full p-2 z-10 text-yellow-600 bg-base-100 bg-opacity-80 text-center">
+9 -4
View File
@@ -1,4 +1,9 @@
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 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
};
+41
View File
@@ -0,0 +1,41 @@
import mermaid from 'mermaid';
// We need to export MermaidConfig and all related types from mermaid.
import type { MermaidConfig } from 'mermaid/dist/config.type';
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`;
};
const initialize = mermaid.initializeAsync({
// logLevel: 0,
lazyLoadedDiagrams: [getDiagramURL('mermaid-mindmap', '9.2.0-rc4')],
loadExternalDiagramsAtStartup: true
});
export const init = async () => {
await initialize;
};
export const render = async (
config: MermaidConfig,
code: string,
id: string,
callback: Parameters<typeof mermaid.render>[2]
): Promise<void> => {
// 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<boolean> => {
await init();
return mermaid.parseAsync(code);
};
+71 -14
View File
@@ -1,13 +1,13 @@
import { writable, get, derived } from 'svelte/store';
import { writable, get, type Readable } from 'svelte/store';
import { persist, localStorage } from './persist';
import { saveStatistics, countLines } from './stats';
import { serializeState, deserializeState } from './serde';
import { cmdKey } from './util';
import mermaid from 'mermaid';
import { asyncable, syncable } from 'svelte-asyncable';
import { cmdKey, errorDebug } from './util';
import { parse } from './mermaid';
import type { Readable } from 'svelte/store';
import type { MarkerData, State, ValidatedState } from '$lib/types';
let count = 0;
export const defaultState: State = {
code: `graph TD
A[Christmas] -->|Get money| B(Go shopping)
@@ -41,8 +41,20 @@ const urlParseFailedState = `graph TD
// inputStateStore handles all updates and is shared externally when exporting via URL, History, etc.
export const inputStateStore = persist(writable(defaultState), localStorage(), 'codeStore');
export let currentState: ValidatedState = (() => {
const state = get(inputStateStore);
return {
...state,
serialized: serializeState(state),
errorMarkers: [],
error: undefined,
editorMode: state.editorMode ?? 'code'
};
})();
// All internal reads should be done via stateStore, but it should not be persisted/shared externally.
export const stateStore: Readable<ValidatedState> = derived([inputStateStore], ([state]) => {
export const stateStore = asyncable(
async (state: State) => {
const processed: ValidatedState = {
...state,
serialized: '',
@@ -51,13 +63,15 @@ export const stateStore: Readable<ValidatedState> = derived([inputStateStore], (
editorMode: state.editorMode ?? 'code'
};
console.log('asyncable', state);
// No changes should be done to fields part of `state`.
try {
processed.serialized = serializeState(state);
mermaid.parse(state.code);
await parse(state.code);
JSON.parse(state.mermaid);
} catch (e) {
processed.error = e;
errorDebug();
console.error(e);
if (e.hash) {
try {
@@ -75,8 +89,48 @@ export const stateStore: Readable<ValidatedState> = derived([inputStateStore], (
}
}
}
currentState = processed;
return processed;
});
},
undefined,
[inputStateStore]
);
// export const stateStore: Readable<ValidatedState> = derived([inputStateStore], ([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);
// mermaid.parse(state.code);
// JSON.parse(state.mermaid);
// } catch (e) {
// processed.error = e;
// 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;
// });
export const loadState = (data: string): void => {
let state: State;
@@ -103,7 +157,7 @@ export const loadState = (data: string): void => {
state.mermaid = defaultState.mermaid;
}
}
updateCodeStore({ ...state });
updateCodeStore(state);
};
export const updateCodeStore = (newState: Partial<State>): void => {
@@ -114,17 +168,18 @@ export const updateCodeStore = (newState: Partial<State>): void => {
};
let prompted = false;
export const updateCode = (
export const updateCode = async (
code: string,
{
updateDiagram = false,
resetPanZoom = false
}: { updateDiagram?: boolean; resetPanZoom?: boolean } = {}
): void => {
): Promise<void> => {
console.log('updateCode', code);
const lines = countLines(code);
saveStatistics(code);
if (lines > 50 && !prompted && get(stateStore).autoSync) {
errorDebug();
if (lines > 50 && !prompted && (await get(stateStore)).autoSync) {
const turnOff = confirm(
`Long diagram detected. Turn off Auto Sync? Use ${cmdKey} + Enter or click the sync logo to manually sync.`
);
@@ -146,6 +201,7 @@ export const updateCode = (
};
export const updateConfig = (config: string): void => {
console.log('updateConfig', config);
inputStateStore.update((state) => {
return { ...state, mermaid: config };
});
@@ -164,7 +220,8 @@ export const toggleDarkTheme = (dark: boolean): void => {
let urlDebounce: number;
export const initURLSubscription = (): void => {
stateStore.subscribe(({ serialized }) => {
stateStore.subscribe(async (state) => {
const { serialized } = await state;
clearTimeout(urlDebounce);
urlDebounce = window.setTimeout(() => {
history.replaceState(undefined, undefined, `#${serialized}`);
+10
View File
@@ -26,3 +26,13 @@ export const initHandler = async (): Promise<void> => {
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
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;
}
};
+29 -26
View File
@@ -54,7 +54,8 @@
};
let docURL = docURLBase;
let activeTabID = 'code';
stateStore.subscribe(({ code, editorMode }: ValidatedState) => {
stateStore.subscribe(async (state) => {
const { code, editorMode } = await state;
activeTabID = editorMode;
const codeTypeMatch = /([\S]+)[\s\n]/.exec(code);
if (codeTypeMatch && codeTypeMatch.length > 1) {
@@ -84,28 +85,30 @@
onMount(async () => {
await initHandler();
const resizer = document.getElementById('resizeHandler');
const element = document.getElementById('editorPane');
const resize = (e: { pageX: number }) => {
const newWidth = e.pageX - element.getBoundingClientRect().left;
if (newWidth > 50) {
element.style.width = `${newWidth}px`;
}
};
const stopResize = () => {
window.removeEventListener('mousemove', resize);
};
resizer.addEventListener('mousedown', (e) => {
e.preventDefault();
window.addEventListener('mousemove', resize);
window.addEventListener('mouseup', stopResize);
});
// const resizer = document.getElementById('resizeHandler');
// const element = document.getElementById('editorPane');
// const resize = (e: { pageX: number }) => {
// const newWidth = e.pageX - element.getBoundingClientRect().left;
// if (newWidth > 50) {
// element.style.width = `${newWidth}px`;
// }
// };
// const stopResize = () => {
// window.removeEventListener('mousemove', resize);
// };
// resizer.addEventListener('mousedown', (e) => {
// e.preventDefault();
// window.addEventListener('mousemove', resize);
// window.addEventListener('mouseup', stopResize);
// });
});
</script>
<div class="h-full flex flex-col overflow-hidden">
<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">
@@ -113,21 +116,21 @@
<div class="form-control flex-row items-center">
<label class="cursor-pointer label" for="autoSync">
<span> Auto sync</span>
<input
<!-- <input
type="checkbox"
class="toggle {$stateStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
class="toggle {state.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
id="autoSync"
bind:checked={$inputStateStore.autoSync} />
bind:checked={$inputStateStore.autoSync} /> -->
</label>
</div>
{#if !$stateStore.autoSync}
<!-- {#if !state.autoSync}
<button
class="btn btn-secondary btn-xs mr-1"
title="Sync Diagram ({cmdKey} + Enter)"
data-cy="sync"
on:click={syncDiagram}><i class="fas fa-sync" /></button>
{/if}
{/if} -->
<button class="btn btn-secondary btn-xs" title="View documentation">
<a target="_blank" rel="noreferrer" href={docURL} data-cy="docs">
@@ -149,21 +152,21 @@
<div class="flex-1 flex flex-col overflow-hidden">
<Card title="Diagram" isCloseable={false}>
<div slot="actions" class="flex flex-row items-center">
<label class="cursor-pointer label py-0" for="panZoom">
<!-- <label class="cursor-pointer label py-0" for="panZoom">
<span>Pan & Zoom</span>
<input
type="checkbox"
class="toggle {$stateStore.panZoom ? 'btn-secondary' : 'toggle-primary'} ml-1"
class="toggle {state.panZoom ? 'btn-secondary' : 'toggle-primary'} ml-1"
id="panZoom"
bind:checked={$inputStateStore.panZoom} />
</label>
<a
href={`${base}/view#${$stateStore.serialized}`}
href={`${base}/view#${state.serialized}`}
target="_blank"
rel="noreferrer"
class="btn btn-secondary btn-xs"
title="View diagram in new page"
><i class="fas fa-external-link-alt mr-1" />Full screen</a>
><i class="fas fa-external-link-alt mr-1" />Full screen</a> -->
</div>
<div class="flex-1 overflow-auto">
+34 -20
View File
@@ -410,11 +410,6 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
"@types/mermaid@9.1.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@types/mermaid/-/mermaid-9.1.0.tgz#e9ba511d8a6793749d6be84f86325f0f58553154"
integrity sha512-rc8QqhveKAY7PouzY/p8ljS+eBSNCv7o79L97RSub/Ic2SQ34ph1Ng3s8wFLWVjvaEt6RLOWtSCsgYWd95NY8A==
"@types/node@*":
version "15.0.2"
resolved "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz"
@@ -2740,6 +2735,11 @@ extsprintf@^1.2.0:
resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-clone@^1.5.13:
version "1.5.13"
resolved "https://registry.yarnpkg.com/fast-clone/-/fast-clone-1.5.13.tgz#7fe17542ae1c872e71bf80d177d00c11f51c2ea7"
integrity sha512-0ez7coyFBQFjZtId+RJqJ+EQs61w9xARfqjqK0AD9vIUkSxWD4HvPt80+5evebZ1tTnv1GYKrPTipx7kOW5ipA==
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
@@ -3672,20 +3672,24 @@ merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
mermaid@9.1.7:
version "9.1.7"
resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-9.1.7.tgz#e24de9b2d36c8cb25a09d72ffce966941b24bd6e"
integrity sha512-MRVHXy5FLjnUQUG7YS3UN9jEN6FXCJbFCXVGJQjVIbiR6Vhw0j/6pLIjqsiah9xoHmQU6DEaKOvB3S1g/1nBPA==
mermaid@9.2.0-rc7:
version "9.2.0-rc7"
resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-9.2.0-rc7.tgz#4f5203f3ff89ecc2bbc79d14c278c47341feb526"
integrity sha512-zudYRTbjsWClH8WR/qz8QmbRjRgctTsg1mmOObUe0OiLZ7+DESQyF4i5IpP4fUXNSIsNuszASL1UFQfeVcyCqg==
dependencies:
"@braintree/sanitize-url" "^6.0.0"
d3 "^7.0.0"
dagre "^0.8.5"
dagre-d3 "^0.6.4"
dompurify "2.4.0"
fast-clone "^1.5.13"
graphlib "^2.1.8"
khroma "^2.0.0"
moment-mini "2.24.0"
stylis "^4.0.10"
lodash "^4.17.21"
moment-mini "^2.24.0"
non-layered-tidy-tree-layout "^2.0.2"
stylis "^4.1.2"
uuid "^9.0.0"
micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.5"
@@ -3746,10 +3750,10 @@ mkdirp@^0.5.1, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
moment-mini@2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.24.0.tgz#fa68d98f7fe93ae65bf1262f6abb5fb6983d8d18"
integrity sha512-9ARkWHBs+6YJIvrIp0Ik5tyTTtP9PoV0Ssu2Ocq5y9v8+NOOpWiRshAp8c4rZVWTOe+157on/5G+zj5pwIQFEQ==
moment-mini@^2.24.0:
version "2.29.4"
resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.29.4.tgz#cbbcdc58ce1b267506f28ea6668dbe060a32758f"
integrity sha512-uhXpYwHFeiTbY9KSgPPRoo1nt8OxNVdMVoTBYHfSEKeRkIkwGpO+gERmhuhBtzfaeOyTkykSrm2+noJBgqt3Hg==
moment@2.29.4:
version "2.29.4"
@@ -3819,6 +3823,11 @@ node-releases@^2.0.6:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
non-layered-tidy-tree-layout@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz#57d35d13c356643fc296a55fb11ac15e74da7804"
integrity sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==
nopt@~4.0.1:
version "4.0.3"
resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz"
@@ -4913,10 +4922,10 @@ stylehacks@^5.1.0:
browserslist "^4.16.6"
postcss-selector-parser "^6.0.4"
stylis@^4.0.10:
version "4.0.10"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==
stylis@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.2.tgz#870b3c1c2275f51b702bb3da9e94eedad87bba41"
integrity sha512-Nn2CCrG2ZaFziDxaZPN43CXqn+j7tcdjPFCkRBkFue8QYXC2HdEwnw5TCBo4yQZ2WxKYeSi0fdoOrtEqgDrXbA==
supports-color@^2.0.0:
version "2.0.0"
@@ -4949,6 +4958,11 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svelte-asyncable@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/svelte-asyncable/-/svelte-asyncable-2.1.0.tgz#cac4d2209135519bc65a4722a0ace89ed568ea2f"
integrity sha512-x9ysuou0dzmwZ4g5bVLL2FmSy/+pugbbm80FmjOvg8mINkA2RENZPF3QKhVdrMKXM9T/PI3oub/J9TsX9lSZMg==
svelte-hmr@^0.14.12:
version "0.14.12"
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.12.tgz#a127aec02f1896500b10148b2d4d21ddde39973f"
@@ -5239,7 +5253,7 @@ util-deprecate@^1.0.2:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
uuid@9.0.0:
uuid@9.0.0, uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==