Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33e27738af | ||
|
|
a02bd4b3ba | ||
|
|
f95c42db1e | ||
|
|
457cd02cc4 | ||
|
|
ed093e3917 |
@@ -3,6 +3,7 @@ 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');
|
||||||
@@ -40,4 +41,17 @@ 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 { serializedState, codeStore } from '$lib/util/state';
|
import { stateStore } from '$lib/util/state';
|
||||||
import { toBase64 } from 'js-base64';
|
import { toBase64 } from 'js-base64';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
@@ -130,10 +130,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
let gistURL = '';
|
let gistURL = '';
|
||||||
codeStore.subscribe((state) => {
|
stateStore.subscribe(({ loader }) => {
|
||||||
if (state.loader?.type === 'gist') {
|
if (loader?.type === 'gist') {
|
||||||
// @ts-ignore Gist will have url
|
// @ts-ignore Gist will have url
|
||||||
gistURL = state.loader.config.url;
|
gistURL = loader.config.url;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -155,11 +155,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;
|
||||||
}
|
}
|
||||||
serializedState.subscribe((encodedState: string) => {
|
stateStore.subscribe(({ code, serialized }) => {
|
||||||
iUrl = `${rendererUrl}/img/${encodedState}`;
|
iUrl = `${rendererUrl}/img/${serialized}`;
|
||||||
svgUrl = `${rendererUrl}/svg/${encodedState}`;
|
svgUrl = `${rendererUrl}/svg/${serialized}`;
|
||||||
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize($codeStore.code)}`;
|
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize(code)}`;
|
||||||
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`;
|
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${serialized})`;
|
||||||
});
|
});
|
||||||
</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 { codeStore } from '$lib/util/state';
|
import { stateStore } 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,17 +21,16 @@
|
|||||||
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 ($codeStore.updateEditor) {
|
if ($stateStore.updateEditor) {
|
||||||
editor?.setValue(text);
|
editor?.setValue(text);
|
||||||
}
|
}
|
||||||
oldText = text;
|
oldText = text;
|
||||||
}
|
}
|
||||||
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
|
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', $stateStore.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 { codeStore, getStateString } from '$lib/util/state';
|
import { inputStateStore, 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: $codeStore,
|
state: $inputStateStore,
|
||||||
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 => {
|
||||||
codeStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
inputStateStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const relativeTime = (time: number) => {
|
const relativeTime = (time: number) => {
|
||||||
|
|||||||
@@ -95,7 +95,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadSampleDiagram = (diagramType: string): void => {
|
const loadSampleDiagram = (diagramType: string): void => {
|
||||||
updateCode(samples[diagramType], true, true);
|
updateCode(samples[diagramType], {
|
||||||
|
updateDiagram: true,
|
||||||
|
updateEditor: true
|
||||||
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { errorStore } from '$lib/util/error';
|
import { inputStateStore, stateStore } from '$lib/util/state';
|
||||||
|
|
||||||
import { codeStore } from '$lib/util/state';
|
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import mermaid from 'mermaid';
|
import mermaid from 'mermaid';
|
||||||
|
|
||||||
@@ -13,11 +11,16 @@
|
|||||||
let outOfSync = false;
|
let outOfSync = false;
|
||||||
let manualUpdate = true;
|
let manualUpdate = true;
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
codeStore.subscribe((state) => {
|
stateStore.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) {
|
||||||
$codeStore.updateDiagram = false;
|
$inputStateStore.updateDiagram = false;
|
||||||
}
|
}
|
||||||
outOfSync = false;
|
outOfSync = false;
|
||||||
manualUpdate = true;
|
manualUpdate = true;
|
||||||
@@ -31,7 +34,10 @@
|
|||||||
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) => {
|
||||||
container.innerHTML = svgCode;
|
if (svgCode.length > 0) {
|
||||||
|
console.log(svgCode);
|
||||||
|
container.innerHTML = svgCode;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
view.parentElement.scrollTop = scroll;
|
view.parentElement.scrollTop = scroll;
|
||||||
error = false;
|
error = false;
|
||||||
@@ -45,24 +51,19 @@
|
|||||||
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
+17
-1
@@ -6,6 +6,16 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -32,6 +42,12 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -64,4 +80,4 @@ export interface DocConfig {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type Loader = (url: string) => Promise<State>;
|
export type Loader = (url: string) => Promise<State>;
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { writable } from 'svelte/store';
|
|
||||||
|
|
||||||
export const errorStore = writable(undefined);
|
|
||||||
@@ -54,5 +54,4 @@ export const loadDataFromUrl = async (): Promise<void> => {
|
|||||||
updateDiagram: true,
|
updateDiagram: true,
|
||||||
updateEditor: true
|
updateEditor: true
|
||||||
});
|
});
|
||||||
// window.location.search = '';
|
|
||||||
};
|
};
|
||||||
|
|||||||
+54
-16
@@ -1,9 +1,11 @@
|
|||||||
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
|
||||||
@@ -36,9 +38,42 @@ 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"`;
|
||||||
|
|
||||||
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
// inputStateStore handles all updates and is shared externally when exporting via URL, History, etc.
|
||||||
export const serializedState: Readable<string> = derived([codeStore], ([code], set) => {
|
export const inputStateStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
||||||
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 => {
|
||||||
@@ -57,30 +92,33 @@ 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(codeStore);
|
state = get(inputStateStore);
|
||||||
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 => {
|
||||||
codeStore.update((state) => {
|
inputStateStore.update((state) => {
|
||||||
return { ...state, ...newState };
|
return { ...state, ...newState };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let prompted = false;
|
let prompted = false;
|
||||||
export const updateCode = (code: string, updateEditor: boolean, updateDiagram = false): void => {
|
export const updateCode = (
|
||||||
|
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(codeStore).autoSync) {
|
if (lines > 50 && !prompted && get(stateStore).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.'
|
||||||
);
|
);
|
||||||
@@ -92,19 +130,19 @@ export const updateCode = (code: string, updateEditor: boolean, updateDiagram =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
codeStore.update((state) => {
|
inputStateStore.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 => {
|
||||||
codeStore.update((state) => {
|
inputStateStore.update((state) => {
|
||||||
return { ...state, mermaid: config, updateEditor };
|
return { ...state, mermaid: config, updateEditor };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toggleDarkTheme = (dark: boolean): void => {
|
export const toggleDarkTheme = (dark: boolean): void => {
|
||||||
codeStore.update((state) => {
|
inputStateStore.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';
|
||||||
@@ -115,11 +153,11 @@ export const toggleDarkTheme = (dark: boolean): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const initURLSubscription = (): void => {
|
export const initURLSubscription = (): void => {
|
||||||
serializedState.subscribe((state: string) => {
|
stateStore.subscribe(({ serialized }) => {
|
||||||
history.replaceState(undefined, undefined, `#${state}`);
|
history.replaceState(undefined, undefined, `#${serialized}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStateString = (): string => {
|
export const getStateString = (): string => {
|
||||||
return JSON.stringify(get(codeStore));
|
return JSON.stringify(get(inputStateStore));
|
||||||
};
|
};
|
||||||
|
|||||||
+18
-53
@@ -6,17 +6,12 @@
|
|||||||
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, codeStore, serializedState } from '$lib/util/state';
|
import { updateCode, updateConfig, inputStateStore, stateStore } 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';
|
||||||
|
|
||||||
@@ -68,17 +63,16 @@
|
|||||||
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 = $codeStore.code;
|
text = $stateStore.code;
|
||||||
} else {
|
} else {
|
||||||
text = $codeStore.mermaid;
|
text = $stateStore.mermaid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
codeStore.subscribe((state: State) => {
|
stateStore.subscribe((state: State) => {
|
||||||
if (state.updateEditor) {
|
if (state.updateEditor) {
|
||||||
text = selectedMode === 'code' ? state.code : state.mermaid;
|
text = selectedMode === 'code' ? state.code : state.mermaid;
|
||||||
}
|
}
|
||||||
@@ -91,7 +85,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';
|
||||||
$codeStore.updateEditor = true;
|
$inputStateStore.updateEditor = true;
|
||||||
};
|
};
|
||||||
const tabs: Tab[] = [
|
const tabs: Tab[] = [
|
||||||
{
|
{
|
||||||
@@ -106,55 +100,26 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
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>) => {
|
||||||
try {
|
const code = message.detail.text;
|
||||||
if (selectedMode === 'code') {
|
if (selectedMode === 'code') {
|
||||||
handleCodeUpdate(message.detail.text);
|
updateCode(code, {
|
||||||
} else {
|
updateEditor: false
|
||||||
handleConfigUpdate(message.detail.text);
|
});
|
||||||
}
|
} else {
|
||||||
errorStore.set(undefined);
|
updateConfig(code, false);
|
||||||
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#${$serializedState}`, '_blank').focus();
|
window.open(`${base}/view#${$stateStore.serialized}`, '_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) => {
|
const resize = (e: { pageX: number }) => {
|
||||||
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`;
|
||||||
@@ -184,13 +149,13 @@
|
|||||||
<span> Auto sync</span>
|
<span> Auto sync</span>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="toggle {$codeStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
|
class="toggle {$stateStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
|
||||||
id="autoSync"
|
id="autoSync"
|
||||||
bind:checked={$codeStore.autoSync} />
|
bind:checked={$inputStateStore.autoSync} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if !$codeStore.autoSync}
|
{#if !$stateStore.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"
|
||||||
@@ -204,7 +169,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Editor on:update={updateHandler} {language} bind:text {errorMarkers} />
|
<Editor on:update={updateHandler} {language} bind:text />
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div class="-mt-2">
|
<div class="-mt-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user