# CM6 Replacement Plan ## Goal Replace Lexical with CodeMirror 6 as the editor engine for Reams. The editor must be markdown-first: canonical markdown text is the source of truth, and visual formatting (heading styles, bold rendering, wikilink chips) is derived via CM6 decorations and widgets. ## Non-Negotiable Constraints 1. **Markdown is canonical**: The CM6 document IS the markdown source. No separate rich-text tree. 2. **Lossless round-trip**: Save and reload must produce identical markdown. 3. **Obsidian plugin API compatibility**: The abstract `Editor` class (`src/plugin/editor.ts`) must be implementable on top of CM6. Plugins use line/offset/selection APIs. 4. **Save-based Automerge sync**: Keep current `useDocumentSync` + `DocumentManager.updateContent()` path. No realtime CM6<->Automerge binding in this spike. 5. **Mobile support**: CM6 has good mobile/touch support out of the box. ## Architecture ### Core Concept CM6 stores the document as a flat text string. Visual formatting is applied via: - **Decorations**: Mark ranges of text with CSS classes (e.g., bold text gets a bold class) - **Widgets**: Insert non-editable elements at positions (e.g., wikilink chips replace `[[target]]` text) - **Line decorations**: Add classes/attributes to line elements (e.g., heading lines get heading-level classes) - **Atomic ranges**: Make certain text ranges behave as single units (e.g., wikilink syntax when collapsed) ### Live Preview Model 1. **Active line**: The line containing the cursor shows raw markdown (syntax visible) 2. **Inactive lines**: Syntax is hidden via decorations, formatted rendering is shown 3. **Proximity reveal**: Inline syntax markers reveal when cursor is within N characters 4. **Wikilink chips**: `[[target]]` is replaced with a widget when cursor is far. Widget is removed and raw text is visible when cursor is near. ### Key Difference from Lexical In Lexical, we had to fight the tree model — creating SyntaxMarkerNodes, managing expand/collapse, preventing transform chains. In CM6, the text is always `**bold**` in the document. We just change what CSS classes and widgets are applied based on cursor position. No tree mutations for visual state changes. ## Migration Map ### Replace | Current (Lexical) | New (CM6) | |---|---| | `src/editor/Editor.tsx` | New CM6 React wrapper component | | `src/editor/SourceEditor.tsx` | CM6 handles both source and live preview (same document, different decorations) | | `src/editor/WysiwygEditor.tsx` | Not needed — CM6 decorations handle visual formatting | | `src/editor/plugins/*.tsx` | CM6 extensions/facets | | `src/editor/nodes/*.tsx` | CM6 widgets + decorations | | `src/editor/markdown/transformers.ts` | Not needed — document IS markdown | | `src/plugin/lexical-editor.ts` | New CM6-backed `Editor` implementation | | `src/plugin/views/markdown-view.ts` | Rework to bind CM6 instead of Lexical | ### Keep (editor-agnostic) | File | Why | |---|---| | `src/plugin/editor.ts` | Abstract Editor class — implement for CM6 | | `src/plugin/types.ts` | EditorPosition, EditorRange, etc. | | `src/services/**` | Document management, search, file index | | `src/sync/**` | Automerge sync hook | | `src/plugin/vault.ts` | File operations | | `src/plugin/workspace.ts` | Workspace/layout management | | `src/plugin/app.ts` | Plugin app instance (needs formatting action changes) | | `src/plugins/**` | Built-in plugins | | `src/layout/**` | App shell, sidebar, tabs | ### Adapt | File | What changes | |---|---| | `src/App.tsx` | Remove Lexical imports, use CM6 editor component | | `src/plugin/views/markdown-view.ts` | Bind CM6 editor instead of Lexical | | `src/plugin/ui/formatting-action-types.ts` | Remove `LexicalEditor` dependency | | `src/plugin/app.ts` | Remove Lexical-specific formatting action creation | | `src/plugins/wikilink/wikilink-plugin.ts` | Remove `WikilinkNode` import, use CM6 widget/decoration API | ## CM6 Extension Architecture ### Planned Extensions 1. **MarkdownSyntaxExtension**: Parses markdown, applies decorations for visual formatting 2. **LivePreviewExtension**: Manages active-line reveal, proximity-based syntax show/hide 3. **WikilinkExtension**: Renders wikilink chips as widgets, handles click navigation 4. **EmbedExtension**: Renders image embeds as widgets 5. **FormattingPairExtension**: Pair deletion for `**`, `*`, `~~`, etc. 6. **ListHelperExtension**: Auto-continue lists, indent/outdent 7. **WikilinkAutocompleteExtension**: `[[` triggers autocomplete ### CM6 Packages Needed - `@codemirror/state` — EditorState, transactions - `@codemirror/view` — EditorView, decorations, widgets - `@codemirror/language` — language support, syntax tree - `@codemirror/lang-markdown` — markdown language support with GFM - `@codemirror/autocomplete` — for wikilink autocomplete - `@codemirror/commands` — basic editing commands - `@codemirror/history` — undo/redo - `@lezer/markdown` — incremental markdown parser ## Spike Proof Targets 1. Heading syntax reveal on active line 2. Inline emphasis reveal by proximity 3. Wikilink chip rendering when far 4. Wikilink editable text when near 5. Clickable wikilinks (open/create file) 6. Save/reload through Automerge 7. Abstract `Editor` class works for existing plugins