Trying out CodeMirror. I think I like it? Use it in readonly mode for view. Wrapping up for the day.

This commit is contained in:
Scott Idem
2025-05-12 19:56:56 -04:00
parent 3b38c31ba0
commit b5642583c7
4 changed files with 1658 additions and 787 deletions

View File

@@ -1,73 +1,118 @@
<script lang="ts">
import { onMount, onDestroy, createEventDispatcher } from 'svelte';
import { EditorView, keymap, placeholder as placeholderExt } from '@codemirror/view';
import { EditorState, StateEffect, type Extension } from '@codemirror/state';
import { indentWithTab } from '@codemirror/commands';
import { basicSetup } from 'codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from "@codemirror/theme-one-dark";
// *** Import Svelte version 5 specific
import { browser } from '$app/environment';
// Props
export let content: string = 'test test test test';
export let new_content: string = '';
export let language: Extension = javascript();
export let theme: Extension = oneDark;
export let extensions: Extension[] = [];
export let editable: boolean = true;
export let placeholder: string = 'Start typing...';
import { onMount, onDestroy, createEventDispatcher } from 'svelte';
import { EditorView, keymap, placeholder as placeholderExt } from '@codemirror/view';
import { EditorState, StateEffect, type Extension } from '@codemirror/state';
import { indentWithTab } from '@codemirror/commands';
import { basicSetup } from 'codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from "@codemirror/theme-one-dark";
const dispatch = createEventDispatcher<{ change: string }>();
// Props
export let content: string = 'test test test test';
export let new_content: string = '';
export let language: Extension = javascript();
export let theme: Extension = oneDark;
export let extensions: Extension[] = [];
export let editable: boolean = true;
export let placeholder: string = 'Start typing...';
let classes = "";
export { classes as class };
let editor_element: HTMLDivElement;
let editorView: EditorView;
let baseTheme = EditorView.baseTheme({
".cm-o-replacement": {
display: "inline-block",
width: ".5em",
height: ".5em",
borderRadius: ".25em"
},
"&light .cm-o-replacement": {
backgroundColor: "#04c"
},
"&dark .cm-o-replacement": {
backgroundColor: "#5bf"
}
})
// theme == baseTheme;
// Reactive declaration for extensions
$: editor_extensions = [
basicSetup,
EditorView.editable.of(editable),
keymap.of([indentWithTab]),
placeholderExt(placeholder),
language,
theme,
...extensions
];
const dispatch = createEventDispatcher<{ change: string }>();
// Initialize CodeMirror on mount
onMount(() => {
editorView = new EditorView({
state: EditorState.create({
doc: content,
extensions: editor_extensions,
}),
parent: editor_element,
dispatch: (transaction) => {
editorView.update([transaction]);
if (transaction.docChanged) {
new_content = editorView.state.doc.toString();
const newContent = editorView.state.doc.toString();
dispatch('change', newContent);
}
let editor_element: HTMLDivElement;
let editorView: EditorView;
// Reactive declaration for extensions
$: editor_extensions = [
basicSetup,
EditorView.editable.of(editable),
keymap.of([indentWithTab]),
placeholderExt(placeholder),
language,
theme,
// baseTheme = {
// ".cm-o-replacement": {
// display: "inline-block",
// width: ".5em",
// height: ".5em",
// borderRadius: ".25em"
// },
// "&light .cm-o-replacement": {
// backgroundColor: "#04c"
// },
// "&dark .cm-o-replacement": {
// backgroundColor: "#5bf"
// }
// },
...extensions
];
// Initialize CodeMirror on mount
onMount(() => {
editorView = new EditorView({
state: EditorState.create({
doc: content,
extensions: editor_extensions,
}),
parent: editor_element,
dispatch: (transaction) => {
editorView.update([transaction]);
if (transaction.docChanged) {
new_content = editorView.state.doc.toString();
const newContent = editorView.state.doc.toString();
dispatch('change', newContent);
}
});
}
});
});
// Clean up on destroy
onDestroy(() => {
editorView?.destroy();
});
// Clean up on destroy
onDestroy(() => {
editorView?.destroy();
});
// Update editor content when `content` prop changes
$: if (editorView && editorView.state.doc.toString() !== content) {
editorView.setState(
EditorState.create({
doc: content,
extensions: editor_extensions
})
);
};
// Update editor content when `content` prop changes
$: if (editorView && editorView.state.doc.toString() !== content) {
editorView.setState(
EditorState.create({
doc: content,
extensions: editor_extensions
})
);
};
</script>
<div bind:this={editor_element} class="codemirror-wrapper"></div>
{#if browser}
<div bind:this={editor_element} class="codemirror-wrapper {classes}"></div>
{:else}
<div class="scm-waiting {classes}">
<div class="scm-waiting__loading scm-loading">
<p class="scm-loading__text">Loading editor...</p>
</div>
<pre class="scm-pre cm-editor">{value}</pre>
</div>
{/if}
<style>
.codemirror-wrapper :global(.cm-focused) {