diff --git a/src/lib/elements/codemirror_modules.ts b/src/lib/elements/codemirror_modules.ts index 138b0468..81b4272b 100644 --- a/src/lib/elements/codemirror_modules.ts +++ b/src/lib/elements/codemirror_modules.ts @@ -81,6 +81,7 @@ type CMCache = { languages?: any; oneDark?: any; placeholderExt?: any; + Compartment?: any; } | null; const GLOBAL_KEY = '__cm_singleton_modules_v1'; @@ -135,6 +136,7 @@ export async function ensure_CodeMirror_modules(): Promise { EditorState_allowMultipleSelections: stateMod.EditorState.allowMultipleSelections, EditorState_readOnly: stateMod.EditorState.readOnly, + Compartment: stateMod.Compartment, markdown: markdownMod?.markdown, markdownLanguage: markdownMod?.markdownLanguage, diff --git a/src/lib/elements/element_editor_codemirror.svelte b/src/lib/elements/element_editor_codemirror.svelte index eed4433e..b52f1d2c 100644 --- a/src/lib/elements/element_editor_codemirror.svelte +++ b/src/lib/elements/element_editor_codemirror.svelte @@ -11,7 +11,7 @@ import { ensure_CodeMirror_modules } from './codemirror_modules'; // import type { key_val } from '$lib/stores/ae_stores'; // Icons (Standardized to Lucide where possible, or FontAwesome placeholders) -import { Bold, Code, Italic, List } from '@lucide/svelte'; +import { Bold, Code, Hash, Italic, List } from '@lucide/svelte'; interface Props { content?: string | null; new_content?: string; @@ -42,6 +42,8 @@ let { let editor_container: HTMLDivElement | undefined = $state(); let cm: any = $state(); // CodeMirror modules cache +let show_line_nums = $state(untrack(() => show_line_numbers)); +let ln_compartment: any = null; async function create_editor() { if (!browser) return; @@ -55,6 +57,8 @@ async function create_editor() { editor_view = null; } + ln_compartment = new cm.Compartment(); + const extensions = [ cm.highlightSpecialChars(), cm.history(), @@ -70,8 +74,9 @@ async function create_editor() { cm.highlightActiveLine(), cm.highlightActiveLineGutter(), - // Keymaps + // Keymaps — indentWithTab must come before defaultKeymap cm.keymap.of([ + cm.indentWithTab, ...cm.defaultKeymap, ...cm.searchKeymap, ...cm.historyKeymap, @@ -80,6 +85,9 @@ async function create_editor() { ...cm.lintKeymap ]), + // 4-space indentation unit + cm.indentUnit.of(' '), + // Language Support language === 'markdown' ? cm.markdown({ base: cm.markdownLanguage }) @@ -94,7 +102,7 @@ async function create_editor() { readonly ? cm.EditorState.readOnly.of(true) : cm.EditorView.editable.of(true), - show_line_numbers ? cm.lineNumbers() : null, + ln_compartment.of(show_line_nums ? cm.lineNumbers() : []), wrap_lines ? cm.EditorView_lineWrapping : null, placeholder ? cm.placeholderExt(placeholder) : null, @@ -144,6 +152,14 @@ $effect(() => { } }); +// Toggle line numbers without rebuilding the editor +$effect(() => { + if (!editor_view || !ln_compartment || !cm) return; + editor_view.dispatch({ + effects: ln_compartment.reconfigure(show_line_nums ? cm.lineNumbers() : []) + }); +}); + // *** Toolbar Helpers const wrap_selection = (before: string, after: string = before) => { if (!editor_view) return; @@ -244,6 +260,15 @@ const toggle_list = () => {
+ {language}