diff --git a/excalidraw-app/vite.config.mts b/excalidraw-app/vite.config.mts index a24d0939a6..fa8d63d956 100644 --- a/excalidraw-app/vite.config.mts +++ b/excalidraw-app/vite.config.mts @@ -106,6 +106,10 @@ export default defineConfig(({ mode }) => { if (id.includes("@excalidraw/mermaid-to-excalidraw")) { return "mermaid-to-excalidraw"; } + + if (id.includes("@codemirror/") || id.includes("@lezer/")) { + return "codemirror.chunk"; + } }, }, }, @@ -150,6 +154,11 @@ export default defineConfig(({ mode }) => { "**/locales/**", "service-worker.js", "**/*.chunk-*.js", + // CodeMirrorEditor can't be assigned a `.chunk` name via + // manualChunks because Rollup would hoist shared deps (React) + // via a static import from the main bundle, defeating lazy + // loading. So we exclude it by name instead. + "**/CodeMirrorEditor-*.js", ], runtimeCaching: [ { @@ -189,7 +198,7 @@ export default defineConfig(({ mode }) => { }, }, { - urlPattern: new RegExp(".chunk-.+.js"), + urlPattern: new RegExp("(.chunk-.+|CodeMirrorEditor-.+)\\.js"), handler: "CacheFirst", options: { cacheName: "chunk", diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index 5bafa41813..6d3858a00d 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -88,7 +88,8 @@ export const isWritableElement = ( (target.type === "text" || target.type === "number" || target.type === "password" || - target.type === "search")); + target.type === "search")) || + (target instanceof HTMLElement && target.closest(".cm-editor") !== null); export const getFontFamilyString = ({ fontFamily, diff --git a/packages/excalidraw/components/TTDDialog/CodeMirrorEditor.tsx b/packages/excalidraw/components/TTDDialog/CodeMirrorEditor.tsx new file mode 100644 index 0000000000..d6866a6121 --- /dev/null +++ b/packages/excalidraw/components/TTDDialog/CodeMirrorEditor.tsx @@ -0,0 +1,239 @@ +import { useEffect, useRef } from "react"; +import { + Decoration, + EditorView, + keymap, + lineNumbers, + placeholder as cmPlaceholder, + drawSelection, +} from "@codemirror/view"; +import { Compartment, EditorState, type Extension } from "@codemirror/state"; +import { + defaultKeymap, + history, + historyKeymap, + redo, +} from "@codemirror/commands"; +import { syntaxHighlighting, HighlightStyle } from "@codemirror/language"; +import { tags } from "@lezer/highlight"; + +import type { Theme } from "@excalidraw/element/types"; + +import { mermaidLite } from "./mermaid-lang-lite"; + +export interface CodeMirrorEditorProps { + value: string; + onChange: (value: string) => void; + onKeyboardSubmit?: () => void; + placeholder?: string; + theme: Theme; + errorLine?: number | null; +} + +// ---- Dark theme ---- + +const darkTheme = EditorView.theme( + { + "&": { + backgroundColor: "#1e1e1e", + color: "#d4d4d4", + }, + ".cm-content": { caretColor: "#fff" }, + ".cm-cursor": { borderLeftColor: "#fff" }, + ".cm-gutters": { + backgroundColor: "#1e1e1e", + color: "#858585", + border: "none", + }, + ".cm-activeLineGutter": { backgroundColor: "#2a2a2a" }, + ".cm-activeLine": { backgroundColor: "#2a2a2a" }, + ".cm-errorLine": { backgroundColor: "rgba(255, 0, 0, 0.15)" }, + }, + { dark: true }, +); + +const darkHighlight = HighlightStyle.define([ + { tag: tags.keyword, color: "#569cd6" }, + { tag: tags.string, color: "#ce9178" }, + { tag: tags.comment, color: "#6a9955" }, + { tag: tags.number, color: "#b5cea8" }, + { tag: tags.operator, color: "#d4d4d4" }, + { tag: tags.punctuation, color: "#d4d4d4" }, + { tag: tags.variableName, color: "#9cdcfe" }, + { tag: tags.bracket, color: "#ffd700" }, +]); + +// ---- Light theme ---- + +const lightTheme = EditorView.theme({ + "&": { + backgroundColor: "#ffffff", + color: "#1e1e1e", + }, + ".cm-content": { caretColor: "#000" }, + ".cm-cursor": { borderLeftColor: "#000" }, + ".cm-gutters": { + backgroundColor: "#fff", + color: "#999", + border: "none", + }, + ".cm-activeLineGutter": { backgroundColor: "#e8e8e8" }, + ".cm-activeLine": { backgroundColor: "#e8e8e8" }, + ".cm-errorLine": { backgroundColor: "rgba(255, 0, 0, 0.1)" }, +}); + +const lightHighlight = HighlightStyle.define([ + { tag: tags.keyword, color: "#0000ff" }, + { tag: tags.string, color: "#a31515" }, + { tag: tags.comment, color: "#008000" }, + { tag: tags.number, color: "#098658" }, + { tag: tags.operator, color: "#1e1e1e" }, + { tag: tags.punctuation, color: "#1e1e1e" }, + { tag: tags.variableName, color: "#001080" }, + { tag: tags.bracket, color: "#af00db" }, +]); + +// ---- Error line decoration ---- + +const errorLineDeco = Decoration.line({ class: "cm-errorLine" }); + +const getErrorLineExtension = ( + errorLine: number | null | undefined, + doc: { line(n: number): { from: number }; lines: number }, +): Extension => { + if (!errorLine || errorLine < 1 || errorLine > doc.lines) { + return EditorView.decorations.of(Decoration.none); + } + const line = doc.line(errorLine); + return EditorView.decorations.of( + Decoration.set([errorLineDeco.range(line.from)]), + ); +}; + +// ---- Helpers ---- + +const getThemeExtensions = (theme: Theme) => { + if (theme === "dark") { + return [darkTheme, syntaxHighlighting(darkHighlight)]; + } + return [lightTheme, syntaxHighlighting(lightHighlight)]; +}; + +const CodeMirrorEditor = ({ + value, + onChange, + onKeyboardSubmit, + placeholder, + theme, + errorLine, +}: CodeMirrorEditorProps) => { + const containerRef = useRef(null); + const viewRef = useRef(null); + const onChangeRef = useRef(onChange); + const onKeyboardSubmitRef = useRef(onKeyboardSubmit); + const themeCompartmentRef = useRef(new Compartment()); + const errorLineCompartmentRef = useRef(new Compartment()); + + onChangeRef.current = onChange; + onKeyboardSubmitRef.current = onKeyboardSubmit; + + useEffect(() => { + if (!containerRef.current) { + return; + } + + const themeCompartment = themeCompartmentRef.current; + + const view = new EditorView({ + state: EditorState.create({ + doc: value, + extensions: [ + keymap.of([ + { + key: "Mod-Enter", + run: () => { + onKeyboardSubmitRef.current?.(); + return true; + }, + }, + // historyKeymap binds Mod-Shift-z only on Mac; add it for all platforms + { key: "Mod-Shift-z", run: redo, preventDefault: true }, + ]), + EditorView.updateListener.of((update) => { + if (update.docChanged) { + onChangeRef.current(update.state.doc.toString()); + } + }), + history(), + keymap.of([...defaultKeymap, ...historyKeymap]), + lineNumbers(), + EditorView.lineWrapping, + themeCompartment.of(getThemeExtensions(theme)), + errorLineCompartmentRef.current.of([]), + mermaidLite(), + drawSelection({ drawRangeCursor: true }), + ...(placeholder ? [cmPlaceholder(placeholder)] : []), + ], + }), + parent: containerRef.current, + }); + + viewRef.current = view; + view.focus(); + + return () => { + view.destroy(); + viewRef.current = null; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // Swap theme dynamically via compartment + useEffect(() => { + const view = viewRef.current; + if (!view) { + return; + } + view.dispatch({ + effects: themeCompartmentRef.current.reconfigure( + getThemeExtensions(theme), + ), + }); + }, [theme]); + + // Update error line highlight + useEffect(() => { + const view = viewRef.current; + if (!view) { + return; + } + view.dispatch({ + effects: errorLineCompartmentRef.current.reconfigure( + getErrorLineExtension(errorLine, view.state.doc), + ), + }); + }, [errorLine]); + + // Sync external value changes into EditorView + useEffect(() => { + const view = viewRef.current; + if (!view) { + return; + } + const currentDoc = view.state.doc.toString(); + if (value !== currentDoc) { + view.dispatch({ + changes: { from: 0, to: currentDoc.length, insert: value }, + }); + } + }, [value]); + + return ( +
+ ); +}; + +export default CodeMirrorEditor; diff --git a/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx b/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx index baa1298026..5f98637f47 100644 --- a/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx +++ b/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx @@ -17,6 +17,11 @@ import { TTDDialogOutput } from "./TTDDialogOutput"; import { TTDDialogPanel } from "./TTDDialogPanel"; import { TTDDialogPanels } from "./TTDDialogPanels"; import { TTDDialogSubmitShortcut } from "./TTDDialogSubmitShortcut"; +import { + getMermaidErrorLineNumber, + isMermaidAutoFixableError, +} from "./utils/mermaidError"; +import { getMermaidAutoFixCandidates } from "./utils/mermaidAutoFix"; import { convertMermaidToExcalidraw, insertToEditor, @@ -33,6 +38,27 @@ const MERMAID_EXAMPLE = "flowchart 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[Car]"; const debouncedSaveMermaidDefinition = debounce(saveMermaidDataToStorage, 300); +const AUTO_FIX_DEBOUNCE_MS = 500; +const AUTO_FIX_MAX_DEPTH = 4; +const AUTO_FIX_MAX_CANDIDATES = 30; + +const getErrorMessage = (error: unknown): string => { + if (error instanceof Error) { + return error.message; + } + if (typeof error === "string") { + return error; + } + if ( + error && + typeof error === "object" && + "message" in error && + typeof (error as { message?: unknown }).message === "string" + ) { + return (error as { message: string }).message; + } + return ""; +}; const MermaidToExcalidraw = ({ mermaidToExcalidrawLib, @@ -46,8 +72,16 @@ const MermaidToExcalidraw = ({ EditorLocalStorage.get(EDITOR_LS_KEYS.MERMAID_TO_EXCALIDRAW) || MERMAID_EXAMPLE, ); - const deferredText = useDeferredValue(text.trim()); + const deferredText = useDeferredValue(text); const [error, setError] = useState(null); + const [autoFixCandidate, setAutoFixCandidate] = useState(null); + + const errorLine = (() => { + if (!error?.message) { + return null; + } + return getMermaidErrorLineNumber(error.message, deferredText); + })(); const canvasRef = useRef(null); const data = useRef<{ @@ -61,7 +95,7 @@ const MermaidToExcalidraw = ({ useEffect(() => { const doRender = async () => { try { - if (!deferredText) { + if (!deferredText.trim()) { resetPreview({ canvasRef, setError }); return; } @@ -98,6 +132,88 @@ const MermaidToExcalidraw = ({ [], ); + useEffect(() => { + const errorMessage = error?.message ?? ""; + const sourceText = deferredText; + const shouldTryAutoFix = + isActive && + isMermaidAutoFixableError(errorMessage) && + !!sourceText.trim() && + mermaidToExcalidrawLib.loaded; + + if (!shouldTryAutoFix) { + setAutoFixCandidate(null); + return; + } + + const candidates = getMermaidAutoFixCandidates(sourceText, errorMessage); + if (!candidates.length) { + setAutoFixCandidate(null); + return; + } + + let cancelled = false; + const timer = setTimeout(async () => { + try { + const api = await mermaidToExcalidrawLib.api; + const seen = new Set([sourceText]); + const queue = candidates.map((candidate) => ({ + text: candidate, + depth: 1, + })); + + let triedCandidates = 0; + + while (queue.length > 0 && triedCandidates < AUTO_FIX_MAX_CANDIDATES) { + const current = queue.shift(); + if (!current || seen.has(current.text)) { + continue; + } + seen.add(current.text); + triedCandidates += 1; + + try { + await api.parseMermaidToExcalidraw(current.text); + if (!cancelled) { + setAutoFixCandidate(current.text); + } + return; + } catch (candidateError) { + if (current.depth >= AUTO_FIX_MAX_DEPTH) { + continue; + } + const nextErrorMessage = getErrorMessage(candidateError); + if (!nextErrorMessage) { + continue; + } + const nextCandidates = getMermaidAutoFixCandidates( + current.text, + nextErrorMessage, + ); + for (const nextCandidate of nextCandidates) { + if (!seen.has(nextCandidate)) { + queue.push({ + text: nextCandidate, + depth: current.depth + 1, + }); + } + } + } + } + } catch { + // ignore auto-fix probe errors + } + if (!cancelled) { + setAutoFixCandidate(null); + } + }, AUTO_FIX_DEBOUNCE_MS); + + return () => { + cancelled = true; + clearTimeout(timer); + }; + }, [deferredText, error?.message, isActive, mermaidToExcalidrawLib]); + const onInsertToEditor = () => { insertToEditor({ app, @@ -107,6 +223,13 @@ const MermaidToExcalidraw = ({ }); }; + const onApplyAutoFix = () => { + if (!autoFixCandidate) { + return; + } + setText(autoFixCandidate); + }; + return ( <>
@@ -130,7 +253,8 @@ const MermaidToExcalidraw = ({ setText(event.target.value)} + onChange={(value) => setText(value)} + errorLine={errorLine} onKeyboardSubmit={() => { onInsertToEditor(); }} @@ -153,6 +277,9 @@ const MermaidToExcalidraw = ({ canvasRef={canvasRef} loaded={mermaidToExcalidrawLib.loaded} error={error} + sourceText={text} + autoFixAvailable={!!autoFixCandidate} + onApplyAutoFix={onApplyAutoFix} /> diff --git a/packages/excalidraw/components/TTDDialog/TTDDialog.scss b/packages/excalidraw/components/TTDDialog/TTDDialog.scss index 3d133c5baf..e2ba3d1a8e 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialog.scss +++ b/packages/excalidraw/components/TTDDialog/TTDDialog.scss @@ -219,6 +219,49 @@ $fullScreenModalBreakpoint: 600px; } } + .ttd-dialog-input--loading { + display: flex; + align-items: center; + justify-content: center; + } + + .ttd-dialog-input--codemirror { + padding: 0; + overflow: hidden; + // Override height:100% from .ttd-dialog-input — use flex sizing + // so the editor fills remaining space without overflowing the panel + height: 0; + flex: 1 1 0; + min-height: 0; + + .cm-editor { + height: 100%; + font-family: monospace; + + &.cm-focused { + outline: none; + } + } + + .cm-scroller { + padding: 0.85rem 0; + overflow: auto; + } + + .cm-gutters { + padding-left: 0.25rem; + } + + .cm-content { + padding: 0; + } + + .cm-placeholder { + color: var(--color-gray-40); + font-style: italic; + } + } + .ttd-dialog-output-wrapper { display: flex; flex-direction: column; @@ -331,14 +374,55 @@ $fullScreenModalBreakpoint: 600px; margin-top: 0.25rem; } + .ttd-dialog-output-error-summary { + width: 100%; + max-width: 640px; + color: var(--color-gray-50); + font-size: 0.9rem; + text-align: left; + + &__headline { + font-weight: 600; + color: var(--color-gray-60); + } + + &__label { + margin-top: 0.35rem; + font-weight: 500; + } + + &__causes { + margin: 0.35rem 0 0; + padding-left: 2rem; + } + } + .ttd-dialog-output-error-message { text-align: left; font-weight: 400; color: var(--color-gray-50); word-break: break-word; white-space: pre-wrap; - max-width: 100%; + max-width: 640px; + width: 100%; font-family: monospace; + + &__caret { + color: var(--color-danger); + } + } + + .ttd-dialog-output-error-autofix-slot { + align-self: flex-start; + margin-top: 0.35rem; + min-height: 2.5rem; + display: flex; + align-items: flex-start; + } + + .ttd-dialog-output-error-autofix { + margin-top: 0; + white-space: nowrap; } } diff --git a/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx b/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx index 24427d52d5..3a14a9fd56 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx +++ b/packages/excalidraw/components/TTDDialog/TTDDialogInput.tsx @@ -1,28 +1,84 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { EVENT, KEYS } from "@excalidraw/common"; -import type { ChangeEventHandler } from "react"; +import Spinner from "../Spinner"; + +import { useUIAppState } from "../../context/ui-appState"; + +import type { ComponentType } from "react"; +import type { CodeMirrorEditorProps } from "./CodeMirrorEditor"; interface TTDDialogInputProps { input: string; placeholder: string; - onChange: ChangeEventHandler; + onChange: (value: string) => void; onKeyboardSubmit?: () => void; + errorLine?: number | null; } +type EditorState = + | { type: "loading" } + | { type: "ready"; component: ComponentType } + | { type: "fallback" }; + +const SPINNER_DELAY_MS = 300; + export const TTDDialogInput = ({ input, placeholder, onChange, onKeyboardSubmit, + errorLine, }: TTDDialogInputProps) => { const ref = useRef(null); const callbackRef = useRef(onKeyboardSubmit); callbackRef.current = onKeyboardSubmit; + const [editorState, setEditorState] = useState({ + type: "loading", + }); + const [showSpinner, setShowSpinner] = useState(false); + + const { theme } = useUIAppState(); + + // Lazy-load CodeMirror editor useEffect(() => { + let cancelled = false; + + const spinnerTimer = setTimeout(() => { + if (!cancelled) { + setShowSpinner(true); + } + }, SPINNER_DELAY_MS); + + import("./CodeMirrorEditor") + .then((mod) => { + if (!cancelled) { + setEditorState({ type: "ready", component: mod.default }); + } + }) + .catch(() => { + if (!cancelled) { + setEditorState({ type: "fallback" }); + } + }) + .finally(() => { + clearTimeout(spinnerTimer); + }); + + return () => { + cancelled = true; + clearTimeout(spinnerTimer); + }; + }, []); + + // Keyboard shortcut + focus for textarea fallback + useEffect(() => { + if (editorState.type !== "fallback") { + return; + } if (!callbackRef.current) { return; } @@ -40,15 +96,42 @@ export const TTDDialogInput = ({ textarea.removeEventListener(EVENT.KEYDOWN, handleKeyDown); }; } - }, []); + }, [editorState.type]); - return ( -
Ctrl
Enter
" -`; +exports[`Test > should open mermaid popup when active tool is mermaid 1`] = `""`; exports[`Test > should show error in preview when mermaid library throws error 1`] = ` "flowchart TD diff --git a/yarn.lock b/yarn.lock index 11de59df7d..5259da6288 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1095,6 +1095,45 @@ resolved "https://registry.yarnpkg.com/@chevrotain/utils/-/utils-11.0.3.tgz#e39999307b102cff3645ec4f5b3665f5297a2224" integrity sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ== +"@codemirror/commands@^6.0.0": + version "6.10.2" + resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.10.2.tgz#338bf53ab146de7bb26da4a1d32c6a6ff4d36b39" + integrity sha512-vvX1fsih9HledO1c9zdotZYUZnE4xV0m6i3m25s5DIfXofuprk6cRcLUZvSk3CASUbwjQX21tOGbkY2BH8TpnQ== + dependencies: + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.4.0" + "@codemirror/view" "^6.27.0" + "@lezer/common" "^1.1.0" + +"@codemirror/language@^6.0.0": + version "6.12.2" + resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.12.2.tgz#7db5a46757411cf251e8f450474c05710c27d42c" + integrity sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg== + dependencies: + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.23.0" + "@lezer/common" "^1.5.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + style-mod "^4.0.0" + +"@codemirror/state@^6.0.0", "@codemirror/state@^6.4.0", "@codemirror/state@^6.5.0": + version "6.5.4" + resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.5.4.tgz#f5be4b8c0d2310180d5f15a9f641c21ca69faf19" + integrity sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw== + dependencies: + "@marijn/find-cluster-break" "^1.0.0" + +"@codemirror/view@^6.0.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0": + version "6.39.16" + resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.39.16.tgz#e9d876aba20b31df7858abd7c2a845319c70b302" + integrity sha512-m6S22fFpKtOWhq8HuhzsI1WzUP/hB9THbDj0Tl5KX4gbO6Y91hwBl7Yky33NdvB6IffuRFiBxf1R8kJMyXmA4Q== + dependencies: + "@codemirror/state" "^6.5.0" + crelt "^1.0.6" + style-mod "^4.1.0" + w3c-keyname "^2.2.4" + "@esbuild/aix-ppc64@0.19.10": version "0.19.10" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.10.tgz#fb3922a0183d27446de00cf60d4f7baaadf98d84" @@ -1492,10 +1531,10 @@ resolved "https://registry.yarnpkg.com/@excalidraw/markdown-to-text/-/markdown-to-text-0.1.2.tgz#1703705e7da608cf478f17bfe96fb295f55a23eb" integrity sha512-1nDXBNAojfi3oSFwJswKREkFm5wrSjqay81QlyRv2pkITG/XYB5v+oChENVBQLcxQwX4IUATWvXM5BcaNhPiIg== -"@excalidraw/mermaid-to-excalidraw@2.0.0-rfc3": - version "2.0.0-rfc3" - resolved "https://registry.yarnpkg.com/@excalidraw/mermaid-to-excalidraw/-/mermaid-to-excalidraw-2.0.0-rfc3.tgz#2aed27280b135086d3d23878e66751819f47c3d4" - integrity sha512-OlKySL2aZwxgvO0wKpjq5fNNWWYwYGQAVMqwG3CJZ/zEf9NotTtX+Rl/WgL6qWvNgDq8/mavOnEstC+42gqnIQ== +"@excalidraw/mermaid-to-excalidraw@2.0.0-rc4": + version "2.0.0-rc4" + resolved "https://registry.yarnpkg.com/@excalidraw/mermaid-to-excalidraw/-/mermaid-to-excalidraw-2.0.0-rc4.tgz#9d38568de8e403fefb6a162efa71e024ea1f7e03" + integrity sha512-92efu7VTYF6appGKgbDJAZEM/YXICpYPYOfl+j1E9SVilIFCJEAg7xH2lt/c44WFtffG+rWKrYwf9KUrPYy1Qw== dependencies: "@excalidraw/markdown-to-text" "0.1.2" "@mermaid-js/parser" "^0.6.3" @@ -2045,6 +2084,30 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@lezer/common@^1.0.0", "@lezer/common@^1.1.0", "@lezer/common@^1.3.0", "@lezer/common@^1.5.0": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.5.1.tgz#6e8c114ff5d36a41148e146a253734d3bb8807d3" + integrity sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw== + +"@lezer/highlight@^1.0.0": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.3.tgz#a20f324b71148a2ea9ba6ff42e58bbfaec702857" + integrity sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g== + dependencies: + "@lezer/common" "^1.3.0" + +"@lezer/lr@^1.0.0": + version "1.4.8" + resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.8.tgz#333de9bc9346057323ff09beb4cda47ccc38a498" + integrity sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA== + dependencies: + "@lezer/common" "^1.0.0" + +"@marijn/find-cluster-break@^1.0.0": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8" + integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g== + "@mermaid-js/parser@^0.6.3": version "0.6.3" resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-0.6.3.tgz#3ce92dad2c5d696d29e11e21109c66a7886c824e" @@ -4968,6 +5031,11 @@ crc-32@^0.3.0: resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-0.3.0.tgz#6a3d3687f5baec41f7e9b99fe1953a2e5d19775e" integrity sha512-kucVIjOmMc1f0tv53BJ/5WIX+MGLcKuoBhnGqQrgKJNqLByb/sVMWfW/Aw6hw0jgcqjJ2pi9E5y32zOIpaUlsA== +crelt@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72" + integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g== + cross-env@7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -9520,6 +9588,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-mod@^4.0.0, style-mod@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.1.3.tgz#6e9012255bb799bdac37e288f7671b5d71bf9f73" + integrity sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ== + styled-jsx@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" @@ -10269,6 +10342,11 @@ vscode-uri@~3.0.8: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== +w3c-keyname@^2.2.4: + version "2.2.8" + resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5" + integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ== + w3c-xmlserializer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"