Files
mermaid-live-editor/src/lib/components/Editor.svelte
T
Sidharth Vinod 99255ab693 Merge branch 'develop' into pr/aloisklink/1496
* develop: (101 commits)
  fix: Flickering when rendering, PanZoom & Rough mode combined
  chore: Simplify queueing logic
  fix: Blank screen on initial load
  chore: Add delay for playground toggle
  fix: Record correct diagram type, send playground toggle event immediately.
  chore(deps): update dependency autoprefixer to v10.4.21
  chore(deps): update all non-major dependencies
  chore(deps): update dependency typescript to v5.8.2
  chore(deps): update all non-major dependencies
  build: update Browserslist db
  Use netlify to redirect users
  chore: Add sitemap to robots.txt
  fix: Add redirect for /index.html
  chore: Add sitemap
  fix: Do not index /view page
  chore(deps): update all non-major dependencies
  feat: Pause banner animation when mouse is on the banner
  fix: Layout for banner
  use snippets
  update urls and styling
  ...
2025-03-14 18:08:52 -07:00

148 lines
4.3 KiB
Svelte

<script lang="ts" module>
declare global {
interface Window {
Cypress: boolean;
editorLoaded: boolean;
}
}
</script>
<script lang="ts">
import type { EditorMode } from '$lib/types';
import { initEditor } from '$lib/util/monacoExtra';
import { stateStore, updateCode, updateConfig } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import { themeStore } from '$lib/util/theme';
import { errorDebug, syncDiagram } from '$lib/util/util';
import * as monaco from 'monaco-editor';
import monacoEditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import monacoJsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import { onDestroy, onMount } from 'svelte';
let divElement: HTMLDivElement | undefined = $state();
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
minimap: {
enabled: false
},
theme: 'mermaid',
overviewRulerLanes: 0
};
let text = '';
stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
// 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.setScrollTop(0);
editor.setValue(newText);
text = newText;
}
// Update editor mode if it's different
const language = editorMode === 'code' ? 'mermaid' : 'json';
const model = editor.getModel();
if (!model) {
console.error("editor model doesn't exist");
return;
}
if (model.getLanguageId() !== language) {
monaco.editor.setModelLanguage(model, language);
}
// Display/clear errors
monaco.editor.setModelMarkers(model, 'mermaid', errorMarkers);
});
themeStore.subscribe(({ isDark }) => {
editor && monaco.editor.setTheme(isDark ? 'mermaid-dark' : 'mermaid');
});
const handleUpdate = (text: string, mode: EditorMode) => {
// console.log('editor HandleUpdate', { text, mode });
if (mode === 'code') {
updateCode(text);
} else {
updateConfig(text);
}
};
onMount(() => {
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') {
return new monacoJsonWorker();
}
return new monacoEditorWorker();
}
};
if (!divElement) {
throw new Error('divEl is undefined');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
initEditor(monaco);
errorDebug();
editor = monaco.editor.create(divElement, editorOptions);
editor.onDidChangeModelContent(({ isFlush }) => {
const newText = editor?.getValue();
if (!newText || text === newText || isFlush) {
return;
}
text = newText;
handleUpdate(text, $stateStore.editorMode);
});
editor.addAction({
id: 'mermaid-render-diagram',
label: 'Render Diagram',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
run: function () {
syncDiagram();
logEvent('renderDiagram', {
method: 'keyboardShortcut'
});
}
});
monaco.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
const resizeObserver = new ResizeObserver((entries) => {
editor?.layout({
height: entries[0].contentRect.height,
width: entries[0].contentRect.width
});
});
if (divElement.parentElement) {
resizeObserver.observe(divElement);
}
if (window.Cypress) {
window.editorLoaded = true;
}
});
onDestroy(() => {
editor?.dispose();
});
</script>
<div class="flex h-full flex-col">
<div bind:this={divElement} id="editor" class="h-full flex-grow overflow-hidden"></div>
{#if $stateStore.error instanceof Error}
<div class="flex flex-col text-sm text-neutral-100">
<div class="flex items-center gap-2 bg-red-700 p-2">
<i class="fa fa-exclamation-circle w-4" aria-hidden="true"></i>
<p>Diagram syntax error</p>
</div>
<output class="max-h-32 overflow-auto bg-red-600 p-2" name="mermaid-error" for="editor">
<pre>{$stateStore.error?.toString()}</pre>
</output>
</div>
{/if}
</div>