Automerge backed Markdown editor for the Atmosphere
reams docs reference cm6-replacement-plan.md
5.3 kB
Markdown
at main

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