Add tab indentation and line number toggle to CodeMirror editor

- Wire indentWithTab into keymap (Tab=indent, Shift-Tab=dedent, 4 spaces)
- Set indentUnit to 4 spaces
- Wrap lineNumbers() in a Compartment for live toggle without editor rebuild
- Add Hash toolbar button to toggle line numbers; respects show_line_numbers prop as initial value

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-05-08 15:55:42 -04:00
parent e3a3ab7de8
commit 60ecd221b4
2 changed files with 30 additions and 3 deletions

View File

@@ -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<CMCache> {
EditorState_allowMultipleSelections:
stateMod.EditorState.allowMultipleSelections,
EditorState_readOnly: stateMod.EditorState.readOnly,
Compartment: stateMod.Compartment,
markdown: markdownMod?.markdown,
markdownLanguage: markdownMod?.markdownLanguage,

View File

@@ -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 = () => {
</button>
<div class="ml-auto flex gap-1">
<button
type="button"
class="btn btn-sm {show_line_nums
? 'variant-filled-primary'
: 'variant-soft'} hover:variant-filled-primary"
onclick={() => (show_line_nums = !show_line_nums)}
title="Toggle Line Numbers">
<Hash size="14" />
</button>
<span
class="mr-2 self-center text-[10px] font-bold uppercase opacity-50"
>{language}</span>