feat: Add syntax highlighting

This commit is contained in:
Sidharth Vinod
2025-04-13 11:57:53 +05:30
parent 89f648ab8f
commit bd1ca7e2ab
4 changed files with 200 additions and 23 deletions
+1
View File
@@ -8,6 +8,7 @@
"corg",
"cssnano",
"esserializer",
"fsegurai",
"gantt",
"gitgraph",
"KROKI",
+5
View File
@@ -83,10 +83,15 @@
},
"dependencies": {
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-markdown": "^6.3.2",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.11.0",
"@codemirror/state": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.36.5",
"@fontsource-variable/recursive": "^5.2.5",
"@fsegurai/codemirror-theme-vscode-dark": "^6.1.2",
"@fsegurai/codemirror-theme-vscode-light": "^6.1.2",
"@mermaid-js/layout-elk": "^0.1.7",
"@mermaid-js/mermaid-zenuml": "^0.2.0",
"codemirror": "^6.0.1",
+154
View File
@@ -11,6 +11,15 @@ importers:
'@codemirror/lang-json':
specifier: ^6.0.1
version: 6.0.1
'@codemirror/lang-markdown':
specifier: ^6.3.2
version: 6.3.2
'@codemirror/lang-yaml':
specifier: ^6.1.2
version: 6.1.2
'@codemirror/language':
specifier: ^6.11.0
version: 6.11.0
'@codemirror/state':
specifier: ^6.5.2
version: 6.5.2
@@ -23,6 +32,12 @@ importers:
'@fontsource-variable/recursive':
specifier: ^5.2.5
version: 5.2.5
'@fsegurai/codemirror-theme-vscode-dark':
specifier: ^6.1.2
version: 6.1.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)(@lezer/highlight@1.2.1)
'@fsegurai/codemirror-theme-vscode-light':
specifier: ^6.1.2
version: 6.1.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)(@lezer/highlight@1.2.1)
'@mermaid-js/layout-elk':
specifier: ^0.1.7
version: 0.1.7(mermaid@11.6.0)
@@ -311,9 +326,24 @@ packages:
'@codemirror/commands@6.8.1':
resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==}
'@codemirror/lang-css@6.3.1':
resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==}
'@codemirror/lang-html@6.4.9':
resolution: {integrity: sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==}
'@codemirror/lang-javascript@6.2.3':
resolution: {integrity: sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==}
'@codemirror/lang-json@6.0.1':
resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==}
'@codemirror/lang-markdown@6.3.2':
resolution: {integrity: sha512-c/5MYinGbFxYl4itE9q/rgN/sMTjOr8XL5OWnC+EaRMLfCbVUmmubTJfdgpfcSS2SCaT7b+Q+xi3l6CgoE+BsA==}
'@codemirror/lang-yaml@6.1.2':
resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==}
'@codemirror/language@6.11.0':
resolution: {integrity: sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==}
@@ -535,6 +565,22 @@ packages:
resolution: {integrity: sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==}
engines: {node: '>=6'}
'@fsegurai/codemirror-theme-vscode-dark@6.1.2':
resolution: {integrity: sha512-boId19KW7GTK9Auj2wJw2Woc2G1p3MTvhZPOtTRryiwjDTG+SWxI62fw2m0bA5U0NzDndGbg/6FwTFg+3b49FA==}
peerDependencies:
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
'@lezer/highlight': ^1.0.0
'@fsegurai/codemirror-theme-vscode-light@6.1.2':
resolution: {integrity: sha512-/RV/om9okBxOPv6SXXnpDwqd1KdZuutBZnUFGhvVHLi2TIRWFpPMagdO8d2uKnEOMyRg/98DN7x3KLE19bSquw==}
peerDependencies:
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
'@lezer/highlight': ^1.0.0
'@headlessui-float/vue@0.14.4':
resolution: {integrity: sha512-MSyWCxUTueeex+veRCf++q4KM/fa4HOe9HDttzGrtgVDBULkGduFK6ItJh7EHJp2U/dY7qpyDUqp2KCHpCEplw==}
peerDependencies:
@@ -610,15 +656,30 @@ packages:
'@lezer/common@1.2.3':
resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==}
'@lezer/css@1.1.11':
resolution: {integrity: sha512-FuAnusbLBl1SEAtfN8NdShxYJiESKw9LAFysfea1T96jD3ydBn12oYjaSG1a04BQRIUd93/0D8e5CV1cUMkmQg==}
'@lezer/highlight@1.2.1':
resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
'@lezer/html@1.3.10':
resolution: {integrity: sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==}
'@lezer/javascript@1.4.21':
resolution: {integrity: sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==}
'@lezer/json@1.0.3':
resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==}
'@lezer/lr@1.4.2':
resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==}
'@lezer/markdown@1.4.2':
resolution: {integrity: sha512-iYewCigG/517D0xJPQd7RGaCjZAFwROiH8T9h7OTtz0bRVtkxzFhGBFJ9JGKgBBs4uuo1cvxzyQ5iKhDLMcLUQ==}
'@lezer/yaml@1.0.3':
resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==}
'@marijn/find-cluster-break@1.0.2':
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
@@ -3938,11 +3999,61 @@ snapshots:
'@codemirror/view': 6.36.5
'@lezer/common': 1.2.3
'@codemirror/lang-css@6.3.1':
dependencies:
'@codemirror/autocomplete': 6.18.6
'@codemirror/language': 6.11.0
'@codemirror/state': 6.5.2
'@lezer/common': 1.2.3
'@lezer/css': 1.1.11
'@codemirror/lang-html@6.4.9':
dependencies:
'@codemirror/autocomplete': 6.18.6
'@codemirror/lang-css': 6.3.1
'@codemirror/lang-javascript': 6.2.3
'@codemirror/language': 6.11.0
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.5
'@lezer/common': 1.2.3
'@lezer/css': 1.1.11
'@lezer/html': 1.3.10
'@codemirror/lang-javascript@6.2.3':
dependencies:
'@codemirror/autocomplete': 6.18.6
'@codemirror/language': 6.11.0
'@codemirror/lint': 6.8.5
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.5
'@lezer/common': 1.2.3
'@lezer/javascript': 1.4.21
'@codemirror/lang-json@6.0.1':
dependencies:
'@codemirror/language': 6.11.0
'@lezer/json': 1.0.3
'@codemirror/lang-markdown@6.3.2':
dependencies:
'@codemirror/autocomplete': 6.18.6
'@codemirror/lang-html': 6.4.9
'@codemirror/language': 6.11.0
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.5
'@lezer/common': 1.2.3
'@lezer/markdown': 1.4.2
'@codemirror/lang-yaml@6.1.2':
dependencies:
'@codemirror/autocomplete': 6.18.6
'@codemirror/language': 6.11.0
'@codemirror/state': 6.5.2
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
'@lezer/yaml': 1.0.3
'@codemirror/language@6.11.0':
dependencies:
'@codemirror/state': 6.5.2
@@ -4117,6 +4228,20 @@ snapshots:
'@fortawesome/fontawesome-free@6.7.2': {}
'@fsegurai/codemirror-theme-vscode-dark@6.1.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)(@lezer/highlight@1.2.1)':
dependencies:
'@codemirror/language': 6.11.0
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.5
'@lezer/highlight': 1.2.1
'@fsegurai/codemirror-theme-vscode-light@6.1.2(@codemirror/language@6.11.0)(@codemirror/state@6.5.2)(@codemirror/view@6.36.5)(@lezer/highlight@1.2.1)':
dependencies:
'@codemirror/language': 6.11.0
'@codemirror/state': 6.5.2
'@codemirror/view': 6.36.5
'@lezer/highlight': 1.2.1
'@headlessui-float/vue@0.14.4(@headlessui/vue@1.7.23(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))':
dependencies:
'@floating-ui/core': 1.6.9
@@ -4205,10 +4330,28 @@ snapshots:
'@lezer/common@1.2.3': {}
'@lezer/css@1.1.11':
dependencies:
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
'@lezer/highlight@1.2.1':
dependencies:
'@lezer/common': 1.2.3
'@lezer/html@1.3.10':
dependencies:
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
'@lezer/javascript@1.4.21':
dependencies:
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
'@lezer/json@1.0.3':
dependencies:
'@lezer/common': 1.2.3
@@ -4219,6 +4362,17 @@ snapshots:
dependencies:
'@lezer/common': 1.2.3
'@lezer/markdown@1.4.2':
dependencies:
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/yaml@1.0.3':
dependencies:
'@lezer/common': 1.2.3
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
'@marijn/find-cluster-break@1.0.2': {}
'@mermaid-js/layout-elk@0.1.7(mermaid@11.6.0)':
+40 -23
View File
@@ -1,27 +1,32 @@
<script lang="ts">
import { stateStore, updateCode, updateConfig } from '$/util/state';
import { json, jsonLanguage } from '@codemirror/lang-json';
import { markdown } from '@codemirror/lang-markdown';
import { yamlFrontmatter } from '@codemirror/lang-yaml';
import { language } from '@codemirror/language';
import { Compartment, EditorState } from '@codemirror/state';
import { oneDark } from '@codemirror/theme-one-dark';
import { EditorView } from '@codemirror/view';
import { vsCodeDark } from '@fsegurai/codemirror-theme-vscode-dark';
import { vsCodeLight } from '@fsegurai/codemirror-theme-vscode-light';
import { basicSetup } from 'codemirror';
import { mode } from 'mode-watcher';
import { onMount } from 'svelte';
let editorView: EditorView;
let editorView: EditorView | undefined;
let editorContainer: HTMLDivElement;
let currentText = $state(
$stateStore.editorMode === 'code' ? $stateStore.code : $stateStore.mermaid
);
let currentText = $state('');
onMount(() => {
const themeCompartment = new Compartment();
const languageCompartment = new Compartment();
editorView = new EditorView({
state: EditorState.create({
doc: currentText,
extensions: [
basicSetup,
themeCompartment.of($mode === 'dark' ? oneDark : []),
languageCompartment.of([]),
themeCompartment.of([]),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
const newText = update.state.doc.toString();
@@ -53,31 +58,43 @@
});
const unsubscribeMode = mode.subscribe((mode) => {
editorView?.dispatch({
effects: themeCompartment.reconfigure(mode === 'dark' ? vsCodeDark : vsCodeLight)
});
});
const unsubscribeState = stateStore.subscribe(({ editorMode, code, mermaid }) => {
const text = editorMode === 'code' ? code : mermaid;
if (currentText === text || !editorView) {
return;
}
currentText = text;
editorView.dispatch({
effects: themeCompartment.reconfigure(mode === 'dark' ? oneDark : [])
changes: {
from: 0,
to: editorView.state.doc.length,
insert: text
}
});
const stateLanguage = editorView.state.facet(language);
const isStateJson = stateLanguage === jsonLanguage;
const isCodeJson = editorMode === 'config';
if (stateLanguage && isStateJson === isCodeJson) {
return;
}
editorView.dispatch({
effects: languageCompartment.reconfigure(
isCodeJson ? json() : yamlFrontmatter({ content: markdown() })
)
});
});
return () => {
unsubscribeMode();
unsubscribeState();
editorView?.destroy();
};
});
stateStore.subscribe(({ editorMode, code, mermaid }) => {
const text = editorMode === 'code' ? code : mermaid;
if (currentText === text) {
return;
}
currentText = text;
editorView?.dispatch({
changes: {
from: 0,
to: editorView.state.doc.length,
insert: text
}
});
});
</script>
<div bind:this={editorContainer} class="h-full w-full"></div>
<div bind:this={editorContainer} class="size-full"></div>