Fold, Hide, & Theming Extensions
Most of ProseMark’s CodeMirror extensions fall into one of three categories:
- Fold Extensions
- Hide Extensions
- Theme & Syntax Highlighting Extensions
Fold Extensions
Section titled “Fold Extensions”These extensions replace part of the markdown text with a CodeMirror widget. This includes the bullets in unordered lists, emojis, horizontal rules, images, and the checkboxes in task lists.
To create your own, you can extend foldableSyntaxFacet like so:
import { Decoration, WidgetType } from '@codemirror/view';import { foldableSyntaxFacet } from '@prosemark/core';
class BulletPoint extends WidgetType { toDOM() { const span = document.createElement('span'); span.className = 'cm-rendered-list-mark'; span.innerHTML = '•'; return span; }
ignoreEvent(_event: Event) { return false; }}
export const myCustomFoldExtension = foldableSyntaxFacet.of({ nodePath: 'BulletList/ListItem/ListMark', // the path selecting the appropriate syntax nodes // Add the decoration onFold: (_state, node) => { // Skip task nodes const cursor = node.node.cursor(); if (cursor.nextSibling() && cursor.name === 'Task') return;
// Create the widget decoration return Decoration.replace({ widget: new BulletPoint() }).range( node.from, node.to, ); },});This will replace all list marks which are inside list items which are inside bullet lists with a widget containing •.
Hide Extensions
Section titled “Hide Extensions”These extensions simply hide syntax elements that shouldn’t be displayed unless the cursor selection includes that syntax. This allows syntax to be accessible, but hidden when not being edited.
To create your own, extend hidableNodeFacet:
import { Decoration } from '@codemirror/view';import { hidableNodeFacet } from '@prosemark/core';
export const myHideExtension = hidableNodeFacet.of({ nodeName: ['StrongEmphasis', 'Emphasis'], subNodeNameToHide: 'EmphasisMark',});This extension hides all EmphasisMark nodes that are inside StrongEmphasis or Emphasis (italics) nodes.
Pointer coordinates (visiblePosAtCoords)
Section titled “Pointer coordinates (visiblePosAtCoords)”Hide marks use CSS font-size: 0, so the characters stay in the document and in CodeMirror’s tile tree, but they collapse to zero width. CodeMirror’s posAtCoords still treats those zero-width boxes as hit targets, which makes caret placement on busy lines (links, nested emphasis) unstable.
There is no CodeMirror facet to replace posAtCoords itself. ProseMark therefore:
- Ships
visiblePosAtCoords— the same closest-horizontally / midpoint rule as CodeMirror, but only over clusters with a positive client box. Use this anywhere a host would callview.posAtCoords(custom handlers, tooltips, etc.). - Installs an internal
mouseSelectionStyleonhideExtensionso normal clicks and drag-selections use that mapper.
When a line has no collapsed clusters, visiblePosAtCoords delegates to CodeMirror unchanged. The pure hit-test helper posFromVisibleCharRects is also exported for tests and advanced hosts.
Theme Extensions
Section titled “Theme Extensions”Hiding syntax isn’t enough to make markdown text look “rendered.” We also need to give it some style. This can be done using theme extensions and syntax highlighting extensions.
Syntax Highlighting
Section titled “Syntax Highlighting”Syntax highlighting extensions apply styles or classes to the text corresponding with various syntax tags. They can be made using @codemirror/language:
import { HighlightStyle, syntaxHighlighting,} from '@codemirror/language';import { markdownTags } from '@prosemark/core';
export syntaxHighlighting( HighlightStyle.define([ { tag: tags.strong, fontWeight: 'bold', }, { tag: markdownTags.headerMark, color: 'var(--pm-header-mark-color)', }, ... ]))The @prosemark/core library provides markdownTags, which has some tags corresponding to markdown-specific syntax nodes like HeaderMark (represented by markdownTags.headerMark). For this to work, additionalMarkdownSyntaxTags from @prosemark/core must be provided as a markdown extension (it’s included in prosemarkMarkdownSyntaxExtensions, as shown on our Getting Started page). If you also render HTML blocks, include renderHtmlMarkdownSyntaxExtensions from @prosemark/render-html so that HTML blocks with multi-line breaks are parsed as single blocks. Dollar-delimited math ($...$ / $$...$$) is parsed by mathMarkdownSyntaxExtension, which is included in prosemarkMarkdownSyntaxExtensions; add latexMarkdownEditorExtensions from @prosemark/latex when you want MathJax rendering for those Math nodes (see the LaTeX math guide).
Theme Extensions
Section titled “Theme Extensions”To manage CSS classes using CodeMirror (especially classes used in various Widgets), use a theme extension:
import { EditorView } from '@codemirror/view';
export const myThemeExtension = EditorView.theme({ '.cm-rendered-link': { textDecoration: 'underline', cursor: 'pointer', color: 'var(--pm-link-color)', }, ...})