Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd17fd7e3f |
Vendored
+15
@@ -0,0 +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}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ describe('Auto sync tests', () => {
|
|||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dim diagram when code is edited', () => {
|
it('should dim diagram when code is edited', () => {
|
||||||
cy.contains('Auto sync').click();
|
cy.contains('Auto sync').click();
|
||||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||||
@@ -41,17 +40,4 @@ describe('Auto sync tests', () => {
|
|||||||
cy.get('#editor').type(`{uparrow}{${cmd}}/`);
|
cy.get('#editor').type(`{uparrow}{${cmd}}/`);
|
||||||
cy.get('#view').contains('Car').should('exist');
|
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('#editor').type(`{enter}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');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import { krokiRendererUrl, rendererUrl } from '$lib/util/env';
|
import { krokiRendererUrl, rendererUrl } from '$lib/util/env';
|
||||||
import { pakoSerde } from '$lib/util/serde';
|
import { pakoSerde } from '$lib/util/serde';
|
||||||
import { stateStore } from '$lib/util/state';
|
import { serializedState, codeStore } from '$lib/util/state';
|
||||||
import { toBase64 } from 'js-base64';
|
import { toBase64 } from 'js-base64';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
const svgEl: HTMLElement = document
|
const svgEl: HTMLElement = document
|
||||||
.querySelector('#container svg')
|
.querySelector('#container svg')
|
||||||
.cloneNode(true) as HTMLElement;
|
.cloneNode(true) as HTMLElement;
|
||||||
|
svgEl.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
||||||
const fontAwesomeCdnUrl = Array.from(document.head.getElementsByTagName('link'))
|
const fontAwesomeCdnUrl = Array.from(document.head.getElementsByTagName('link'))
|
||||||
.map((l) => l.href)
|
.map((l) => l.href)
|
||||||
.find((h) => h && h.includes('font-awesome'));
|
.find((h) => h && h.includes('font-awesome'));
|
||||||
@@ -130,10 +131,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
let gistURL = '';
|
let gistURL = '';
|
||||||
stateStore.subscribe(({ loader }) => {
|
codeStore.subscribe((state) => {
|
||||||
if (loader?.type === 'gist') {
|
if (state.loader?.type === 'gist') {
|
||||||
// @ts-ignore Gist will have url
|
// @ts-ignore Gist will have url
|
||||||
gistURL = loader.config.url;
|
gistURL = state.loader.config.url;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -155,11 +156,11 @@
|
|||||||
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
|
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
|
||||||
isNetlify = true;
|
isNetlify = true;
|
||||||
}
|
}
|
||||||
stateStore.subscribe(({ code, serialized }) => {
|
serializedState.subscribe((encodedState: string) => {
|
||||||
iUrl = `${rendererUrl}/img/${serialized}`;
|
iUrl = `${rendererUrl}/img/${encodedState}`;
|
||||||
svgUrl = `${rendererUrl}/svg/${serialized}`;
|
svgUrl = `${rendererUrl}/svg/${encodedState}`;
|
||||||
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize(code)}`;
|
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize($codeStore.code)}`;
|
||||||
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${serialized})`;
|
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { EditorEvents } from '$lib/types';
|
import type { EditorEvents } from '$lib/types';
|
||||||
import { stateStore } from '$lib/util/state';
|
import { codeStore } from '$lib/util/state';
|
||||||
import { themeStore } from '$lib/util/theme';
|
import { themeStore } from '$lib/util/theme';
|
||||||
import type monaco from 'monaco-editor';
|
import type monaco from 'monaco-editor';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
@@ -21,16 +21,17 @@
|
|||||||
theme: 'mermaid',
|
theme: 'mermaid',
|
||||||
overviewRulerLanes: 0
|
overviewRulerLanes: 0
|
||||||
};
|
};
|
||||||
|
export let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||||
let oldText = text;
|
let oldText = text;
|
||||||
$: editor && Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
$: editor && Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
||||||
$: {
|
$: {
|
||||||
if (text !== oldText) {
|
if (text !== oldText) {
|
||||||
if ($stateStore.updateEditor) {
|
if ($codeStore.updateEditor) {
|
||||||
editor?.setValue(text);
|
editor?.setValue(text);
|
||||||
}
|
}
|
||||||
oldText = text;
|
oldText = text;
|
||||||
}
|
}
|
||||||
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', $stateStore.errorMarkers);
|
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
|
||||||
}
|
}
|
||||||
|
|
||||||
themeStore.subscribe(({ isDark }) => {
|
themeStore.subscribe(({ isDark }) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import { inputStateStore, getStateString } from '$lib/util/state';
|
import { codeStore, getStateString } from '$lib/util/state';
|
||||||
import {
|
import {
|
||||||
addHistoryEntry,
|
addHistoryEntry,
|
||||||
historyModeStore,
|
historyModeStore,
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
const previousState: string = getPreviousState(auto);
|
const previousState: string = getPreviousState(auto);
|
||||||
if (previousState !== currentState) {
|
if (previousState !== currentState) {
|
||||||
addHistoryEntry({
|
addHistoryEntry({
|
||||||
state: $inputStateStore,
|
state: $codeStore,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
type: auto ? 'auto' : 'manual'
|
type: auto ? 'auto' : 'manual'
|
||||||
});
|
});
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const restoreHistory = (state: State): void => {
|
const restoreHistory = (state: State): void => {
|
||||||
inputStateStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
codeStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const relativeTime = (time: number) => {
|
const relativeTime = (time: number) => {
|
||||||
|
|||||||
@@ -95,10 +95,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadSampleDiagram = (diagramType: string): void => {
|
const loadSampleDiagram = (diagramType: string): void => {
|
||||||
updateCode(samples[diagramType], {
|
updateCode(samples[diagramType], true, true);
|
||||||
updateDiagram: true,
|
|
||||||
updateEditor: true
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { inputStateStore, stateStore } from '$lib/util/state';
|
import { errorStore } from '$lib/util/error';
|
||||||
|
|
||||||
|
import { codeStore } from '$lib/util/state';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import mermaid from 'mermaid';
|
import mermaid from 'mermaid';
|
||||||
|
|
||||||
@@ -11,16 +13,11 @@
|
|||||||
let outOfSync = false;
|
let outOfSync = false;
|
||||||
let manualUpdate = true;
|
let manualUpdate = true;
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
stateStore.subscribe((state) => {
|
codeStore.subscribe((state) => {
|
||||||
if (state.error !== undefined) {
|
|
||||||
error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error = false;
|
|
||||||
try {
|
try {
|
||||||
if (container && state && (state.updateDiagram || state.autoSync)) {
|
if (container && state && (state.updateDiagram || state.autoSync)) {
|
||||||
if (!state.autoSync) {
|
if (!state.autoSync) {
|
||||||
$inputStateStore.updateDiagram = false;
|
$codeStore.updateDiagram = false;
|
||||||
}
|
}
|
||||||
outOfSync = false;
|
outOfSync = false;
|
||||||
manualUpdate = true;
|
manualUpdate = true;
|
||||||
@@ -34,10 +31,7 @@
|
|||||||
delete container.dataset.processed;
|
delete container.dataset.processed;
|
||||||
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
|
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
|
||||||
mermaid.render('graph-div', code, (svgCode) => {
|
mermaid.render('graph-div', code, (svgCode) => {
|
||||||
if (svgCode.length > 0) {
|
container.innerHTML = svgCode;
|
||||||
console.log(svgCode);
|
|
||||||
container.innerHTML = svgCode;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
view.parentElement.scrollTop = scroll;
|
view.parentElement.scrollTop = scroll;
|
||||||
error = false;
|
error = false;
|
||||||
@@ -51,19 +45,24 @@
|
|||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
errorStore.subscribe((err) => {
|
||||||
|
if (typeof err === 'undefined') {
|
||||||
|
error = false;
|
||||||
|
} else {
|
||||||
|
error = true;
|
||||||
|
console.log('Error: ', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if error && $stateStore.error instanceof Error}
|
|
||||||
<div class="p-2 text-red-600" id="errorContainer">{$stateStore.error}</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div id="view" bind:this={view} class="p-2" class:error class:outOfSync>
|
<div id="view" bind:this={view} class="p-2" class:error class:outOfSync>
|
||||||
<div id="container" bind:this={container} class="flex-1 overflow-auto" />
|
<div id="container" bind:this={container} class="flex-1 overflow-auto" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#view {
|
#view {
|
||||||
|
border: 1px solor darkred;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.error,
|
.error,
|
||||||
|
|||||||
Vendored
+1
-17
@@ -6,16 +6,6 @@ export interface Locals {
|
|||||||
userid: string;
|
userid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkerData {
|
|
||||||
severity: number;
|
|
||||||
message: string;
|
|
||||||
source?: string;
|
|
||||||
startLineNumber: number;
|
|
||||||
startColumn: number;
|
|
||||||
endLineNumber: number;
|
|
||||||
endColumn: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EditorUpdateEvent {
|
export interface EditorUpdateEvent {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
@@ -42,12 +32,6 @@ export interface State {
|
|||||||
loader?: LoaderConfig;
|
loader?: LoaderConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValidatedState extends State {
|
|
||||||
error: any;
|
|
||||||
errorMarkers: MarkerData[];
|
|
||||||
serialized: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GistLoaderConfig {
|
export interface GistLoaderConfig {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
@@ -80,4 +64,4 @@ export interface DocConfig {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Loader = (url: string) => Promise<State>;
|
type Loader = (url: string) => Promise<State>;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const errorStore = writable(undefined);
|
||||||
@@ -54,4 +54,5 @@ export const loadDataFromUrl = async (): Promise<void> => {
|
|||||||
updateDiagram: true,
|
updateDiagram: true,
|
||||||
updateEditor: true
|
updateEditor: true
|
||||||
});
|
});
|
||||||
|
// window.location.search = '';
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-54
@@ -1,11 +1,9 @@
|
|||||||
import { writable, get, derived } from 'svelte/store';
|
import { writable, get, derived } from 'svelte/store';
|
||||||
|
import type { Readable } from 'svelte/store';
|
||||||
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
||||||
|
import type { State } from '$lib/types';
|
||||||
import { saveStatistics } from './stats';
|
import { saveStatistics } from './stats';
|
||||||
import { serializeState, deserializeState } from './serde';
|
import { serializeState, deserializeState } from './serde';
|
||||||
import mermaid from 'mermaid';
|
|
||||||
|
|
||||||
import type { Readable } from 'svelte/store';
|
|
||||||
import type { MarkerData, State, ValidatedState } from '$lib/types';
|
|
||||||
|
|
||||||
export const defaultState: State = {
|
export const defaultState: State = {
|
||||||
code: `graph TD
|
code: `graph TD
|
||||||
@@ -38,42 +36,9 @@ const urlParseFailedState = `graph TD
|
|||||||
G --> |"No :("| H(Try using the Timeline tab in History <br/>from same browser you used to create the diagram.)
|
G --> |"No :("| H(Try using the Timeline tab in History <br/>from same browser you used to create the diagram.)
|
||||||
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
|
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
|
||||||
|
|
||||||
// inputStateStore handles all updates and is shared externally when exporting via URL, History, etc.
|
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
||||||
export const inputStateStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
export const serializedState: Readable<string> = derived([codeStore], ([code], set) => {
|
||||||
|
set(serializeState(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]) => {
|
|
||||||
const processed: ValidatedState = {
|
|
||||||
...state,
|
|
||||||
serialized: '',
|
|
||||||
errorMarkers: [],
|
|
||||||
error: undefined
|
|
||||||
};
|
|
||||||
// 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 => {
|
export const loadState = (data: string): void => {
|
||||||
@@ -92,33 +57,30 @@ export const loadState = (data: string): void => {
|
|||||||
) {
|
) {
|
||||||
delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
|
delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
|
||||||
}
|
}
|
||||||
|
|
||||||
state.mermaid = JSON.stringify(mermaidConfig, null, 2);
|
state.mermaid = JSON.stringify(mermaidConfig, null, 2);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
state = get(inputStateStore);
|
state = get(codeStore);
|
||||||
if (data) {
|
if (data) {
|
||||||
console.error('Init error', e);
|
console.error('Init error', e);
|
||||||
state.code = urlParseFailedState;
|
state.code = urlParseFailedState;
|
||||||
state.mermaid = defaultState.mermaid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateCodeStore({ ...state, updateEditor: true });
|
updateCodeStore({ ...state, updateEditor: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateCodeStore = (newState: State): void => {
|
export const updateCodeStore = (newState: State): void => {
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
return { ...state, ...newState };
|
return { ...state, ...newState };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let prompted = false;
|
let prompted = false;
|
||||||
export const updateCode = (
|
export const updateCode = (code: string, updateEditor: boolean, updateDiagram = false): void => {
|
||||||
code: string,
|
|
||||||
{ updateEditor, updateDiagram = false }: { updateEditor: boolean; updateDiagram?: boolean }
|
|
||||||
): void => {
|
|
||||||
saveStatistics(code);
|
saveStatistics(code);
|
||||||
const lines = (code.match(/\n/g) || '').length + 1;
|
const lines = (code.match(/\n/g) || '').length + 1;
|
||||||
|
|
||||||
if (lines > 50 && !prompted && get(stateStore).autoSync) {
|
if (lines > 50 && !prompted && get(codeStore).autoSync) {
|
||||||
const turnOff = confirm(
|
const turnOff = confirm(
|
||||||
'Long diagram detected. Turn off Auto Sync? Click the sync logo to manually sync.'
|
'Long diagram detected. Turn off Auto Sync? Click the sync logo to manually sync.'
|
||||||
);
|
);
|
||||||
@@ -130,19 +92,19 @@ export const updateCode = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
return { ...state, code, updateEditor, updateDiagram };
|
return { ...state, code, updateEditor, updateDiagram };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateConfig = (config: string, updateEditor: boolean): void => {
|
export const updateConfig = (config: string, updateEditor: boolean): void => {
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
return { ...state, mermaid: config, updateEditor };
|
return { ...state, mermaid: config, updateEditor };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toggleDarkTheme = (dark: boolean): void => {
|
export const toggleDarkTheme = (dark: boolean): void => {
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
const config = JSON.parse(state.mermaid);
|
const config = JSON.parse(state.mermaid);
|
||||||
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
|
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
|
||||||
config.theme = dark ? 'dark' : 'default';
|
config.theme = dark ? 'dark' : 'default';
|
||||||
@@ -153,11 +115,11 @@ export const toggleDarkTheme = (dark: boolean): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const initURLSubscription = (): void => {
|
export const initURLSubscription = (): void => {
|
||||||
stateStore.subscribe(({ serialized }) => {
|
serializedState.subscribe((state: string) => {
|
||||||
history.replaceState(undefined, undefined, `#${serialized}`);
|
history.replaceState(undefined, undefined, `#${state}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStateString = (): string => {
|
export const getStateString = (): string => {
|
||||||
return JSON.stringify(get(inputStateStore));
|
return JSON.stringify(get(codeStore));
|
||||||
};
|
};
|
||||||
|
|||||||
+53
-18
@@ -6,12 +6,17 @@
|
|||||||
import View from '$lib/components/view.svelte';
|
import View from '$lib/components/view.svelte';
|
||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import History from '$lib/components/history/history.svelte';
|
import History from '$lib/components/history/history.svelte';
|
||||||
import { updateCode, updateConfig, inputStateStore, stateStore } from '$lib/util/state';
|
import { updateCode, updateConfig, codeStore, serializedState } from '$lib/util/state';
|
||||||
import { initHandler, syncDiagram } from '$lib/util/util';
|
import { initHandler, syncDiagram } from '$lib/util/util';
|
||||||
|
import { errorStore } from '$lib/util/error';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import mermaid from 'mermaid';
|
||||||
|
import type monaco from 'monaco-editor';
|
||||||
import type { EditorUpdateEvent, State, Tab, DocConfig } from '$lib/types';
|
import type { EditorUpdateEvent, State, Tab, DocConfig } from '$lib/types';
|
||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
|
serializedState; // Weird fix for error > serializedState is not defined. Treeshaking?
|
||||||
|
|
||||||
type Modes = 'code' | 'config';
|
type Modes = 'code' | 'config';
|
||||||
type Languages = 'mermaid' | 'json';
|
type Languages = 'mermaid' | 'json';
|
||||||
|
|
||||||
@@ -63,16 +68,17 @@
|
|||||||
let text = '';
|
let text = '';
|
||||||
let docURL = docURLBase;
|
let docURL = docURLBase;
|
||||||
let language: Languages = 'mermaid';
|
let language: Languages = 'mermaid';
|
||||||
|
let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||||
$: language = languageMap[selectedMode];
|
$: language = languageMap[selectedMode];
|
||||||
$: {
|
$: {
|
||||||
if (selectedMode === 'code') {
|
if (selectedMode === 'code') {
|
||||||
text = $stateStore.code;
|
text = $codeStore.code;
|
||||||
} else {
|
} else {
|
||||||
text = $stateStore.mermaid;
|
text = $codeStore.mermaid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stateStore.subscribe((state: State) => {
|
codeStore.subscribe((state: State) => {
|
||||||
if (state.updateEditor) {
|
if (state.updateEditor) {
|
||||||
text = selectedMode === 'code' ? state.code : state.mermaid;
|
text = selectedMode === 'code' ? state.code : state.mermaid;
|
||||||
}
|
}
|
||||||
@@ -85,7 +91,7 @@
|
|||||||
});
|
});
|
||||||
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
||||||
selectedMode = message.detail.id === 'code' ? 'code' : 'config';
|
selectedMode = message.detail.id === 'code' ? 'code' : 'config';
|
||||||
$inputStateStore.updateEditor = true;
|
$codeStore.updateEditor = true;
|
||||||
};
|
};
|
||||||
const tabs: Tab[] = [
|
const tabs: Tab[] = [
|
||||||
{
|
{
|
||||||
@@ -100,26 +106,55 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const handleCodeUpdate = (code: string): void => {
|
||||||
|
mermaid.parse(code);
|
||||||
|
updateCode(code, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfigUpdate = (config: string): void => {
|
||||||
|
JSON.parse(config);
|
||||||
|
updateConfig(config, false);
|
||||||
|
};
|
||||||
|
|
||||||
const updateHandler = (message: CustomEvent<EditorUpdateEvent>) => {
|
const updateHandler = (message: CustomEvent<EditorUpdateEvent>) => {
|
||||||
const code = message.detail.text;
|
try {
|
||||||
if (selectedMode === 'code') {
|
if (selectedMode === 'code') {
|
||||||
updateCode(code, {
|
handleCodeUpdate(message.detail.text);
|
||||||
updateEditor: false
|
} else {
|
||||||
});
|
handleConfigUpdate(message.detail.text);
|
||||||
} else {
|
}
|
||||||
updateConfig(code, false);
|
errorStore.set(undefined);
|
||||||
|
errorMarkers = [];
|
||||||
|
} catch (e) {
|
||||||
|
errorStore.set(e);
|
||||||
|
if (e.hash) {
|
||||||
|
const marker: monaco.editor.IMarkerData = {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
errorMarkers.push(marker);
|
||||||
|
// Clear all previous errors before this error.
|
||||||
|
errorMarkers = errorMarkers.filter(
|
||||||
|
(m) => m.startLineNumber >= marker.startLineNumber && m.startColumn >= marker.startColumn
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewDiagram = () => {
|
const viewDiagram = () => {
|
||||||
window.open(`${base}/view#${$stateStore.serialized}`, '_blank').focus();
|
window.open(`${base}/view#${$serializedState}`, '_blank').focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await initHandler();
|
await initHandler();
|
||||||
const resizer = document.getElementById('resizeHandler');
|
const resizer = document.getElementById('resizeHandler');
|
||||||
const element = document.getElementById('editorPane');
|
const element = document.getElementById('editorPane');
|
||||||
const resize = (e: { pageX: number }) => {
|
const resize = (e) => {
|
||||||
const newWidth = e.pageX - element.getBoundingClientRect().left;
|
const newWidth = e.pageX - element.getBoundingClientRect().left;
|
||||||
if (newWidth > 50) {
|
if (newWidth > 50) {
|
||||||
element.style.width = `${newWidth}px`;
|
element.style.width = `${newWidth}px`;
|
||||||
@@ -149,13 +184,13 @@
|
|||||||
<span> Auto sync</span>
|
<span> Auto sync</span>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="toggle {$stateStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
|
class="toggle {$codeStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
|
||||||
id="autoSync"
|
id="autoSync"
|
||||||
bind:checked={$inputStateStore.autoSync} />
|
bind:checked={$codeStore.autoSync} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if !$stateStore.autoSync}
|
{#if !$codeStore.autoSync}
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary btn-xs mr-1"
|
class="btn btn-secondary btn-xs mr-1"
|
||||||
title="Sync Diagram"
|
title="Sync Diagram"
|
||||||
@@ -169,7 +204,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Editor on:update={updateHandler} {language} bind:text />
|
<Editor on:update={updateHandler} {language} bind:text {errorMarkers} />
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div class="-mt-2">
|
<div class="-mt-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user