Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
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 #
- Markdown is canonical: The CM6 document IS the markdown source. No separate rich-text tree.
- Lossless round-trip: Save and reload must produce identical markdown.
- Obsidian plugin API compatibility: The abstract
Editorclass (src/plugin/editor.ts) must be implementable on top of CM6. Plugins use line/offset/selection APIs. - Save-based Automerge sync: Keep current
useDocumentSync+DocumentManager.updateContent()path. No realtime CM6<->Automerge binding in this spike. - 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 #
- Active line: The line containing the cursor shows raw markdown (syntax visible)
- Inactive lines: Syntax is hidden via decorations, formatted rendering is shown
- Proximity reveal: Inline syntax markers reveal when cursor is within N characters
- 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 #
- MarkdownSyntaxExtension: Parses markdown, applies decorations for visual formatting
- LivePreviewExtension: Manages active-line reveal, proximity-based syntax show/hide
- WikilinkExtension: Renders wikilink chips as widgets, handles click navigation
- EmbedExtension: Renders image embeds as widgets
- FormattingPairExtension: Pair deletion for
**,*,~~, etc. - ListHelperExtension: Auto-continue lists, indent/outdent
- 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 #
- Heading syntax reveal on active line
- Inline emphasis reveal by proximity
- Wikilink chip rendering when far
- Wikilink editable text when near
- Clickable wikilinks (open/create file)
- Save/reload through Automerge
- Abstract
Editorclass works for existing plugins