# Host integration These examples target `@conql/codemirror-live-markdown@1.0.0`. The editor owns Markdown, selections and transactions. The host owns note lookup, storage, navigation and renderer loading. ## Modes and themes ```ts import { Compartment } from '@codemirror/state'; import { EditorView } from '@codemirror/view'; import { liveMarkdown } from '@conql/codemirror-live-markdown'; const features = new Compartment(); const view = new EditorView({ parent: document.querySelector('#editor')!, doc: '# Note', extensions: [features.of(liveMarkdown())], }); view.dispatch({ effects: features.reconfigure(liveMarkdown({ mode: 'reading', theme: 'dark' })), }); ``` Reconfiguration preserves the Markdown document. Preserve your host options when rebuilding the preset. Replacing an attachment extension cancels its pending saves; finish or cancel them before changing documents. Code blocks show a language label without MD or Copy controls by default. Clicking inline code content reveals editable fences; native text selection and copying remain available. Hosts can opt into a copy button with `codeBlocks: { copyButton: true }`. The explicit `interaction: 'toggle'` mode retains its source switch. Quotes reserve a separate slot for each `>` prefix. Inactive markers are transparent native text; revealing them does not move the body or change wrapping. Quote folding controls sit outside the border. Lists with a full indentation group keep their folding controls beside the nested marker; shallow quoted lists use the outer quote gutter to avoid colliding with source prefixes. Nested lists and tasks display indentation guides in live and reading modes. Each full indentation group (four spaces or a Tab with the default tab size) reserves `--md-list-step` (default `2.25em`) and owns one guide; partial groups retain their spaces without an extra guide. Override `--md-indent-guide` to change the guide color. Inline code uses its own rounded background, customizable with `--md-inline-code-bg`; existing `--md-code-bg` overrides remain a fallback for both inline and fenced code. ## Existing CodeMirror setup ```ts import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; import { obsidianMarkdown, editorInteractions, livePreviewPlugin, markdownStylePlugin, editorTheme, codeBlockField, tableEditorPlugin, calloutPlugin, footnotePlugin, } from '@conql/codemirror-live-markdown'; const extensions = [ markdown({ base: markdownLanguage, extensions: [obsidianMarkdown] }), editorInteractions, livePreviewPlugin, // Includes listPlugin for compatibility. markdownStylePlugin, calloutPlugin, footnotePlugin, codeBlockField({ interaction: 'inline' }), tableEditorPlugin(), editorTheme, ]; ``` Add these beside your existing history, search and keymaps. Use one Markdown parser and one renderer per block type. Set `markdownModeFacet.of('reading')` together with CodeMirror's read-only/editable facets if composing reading mode yourself. ## Internal links and completion ```ts liveMarkdown({ links: { onWikiLinkClick: target => app.openNote(target), }, wikiLinks: async (query, signal) => { const notes = await app.findNotes(query, { signal }); return notes.map(note => ({ target: note.path, label: note.title, detail: note.folder, })); }, }); ``` `app` is your host service. Static arrays are also supported. Targets can include headings and block IDs; opening and resolving those targets belongs to the host. Completion does not duplicate an existing `]]`. Async sources receive an abort signal and are queried again as the text changes. In live mode, an ordinary link click enters source; Ctrl/Cmd-click opens it. Reading-mode clicks open links normally. Existing `onLinkClick` and `onWikiLinkClick` callbacks are retained. Without a wiki callback, no note application is opened. ## Attachments and images ```ts liveMarkdown({ attachments: { save: (file, { signal }) => app.saveAttachment(file, { signal }), onError: (error, file) => app.showError(`Could not save ${file.name}`, error), }, images: { resolvePath: path => app.attachmentURL(path), maxWidth: '100%', }, }); ``` `save` returns a stable Markdown path or URL. Image files become `![name](path)`; other files become `[name](path)`. Paste inserts at the end of the main selection without deleting selected text; drop inserts at the pointer. Saving a group preserves file order and produces one separately undoable transaction. The insertion follows edits made while saving. Deleting its anchor, entering read-only mode, or destroying/removing the extension cancels the insertion. Honor `signal` in storage code. Cancellation prevents an editor insertion; it cannot undo a file already persisted by the host. Failed saves call `onError` and leave the document intact. A host can show upload progress using its save callback; the library does not insert temporary Markdown placeholders. Image syntax supports normal Markdown and image-file Wiki embeds such as `![[assets/photo.png|320x180]]` or `![[photo.png|320]]`. The optional `resolvePath` may return a URL synchronously or asynchronously. The host is responsible for object-URL lifetime. Non-image note embeds remain source text. ## Optional Mermaid or other fenced previews Install Mermaid in the host, then provide a renderer. No diagram runtime is bundled into the library: ```ts import mermaid from 'mermaid'; mermaid.initialize({ startOnLoad: false, securityLevel: 'strict' }); let diagramId = 0; liveMarkdown({ codeRenderers: { mermaid: async (source, { signal }) => { const { svg } = await mermaid.render(`md-diagram-${diagramId++}`, source); const element = document.createElement('div'); if (!signal.aborted) element.innerHTML = svg; return element; }, }, }); ``` Renderer keys are lowercase fence languages. Return a fresh DOM node for each preview. The host must sanitize HTML and configure trusted rendering appropriately. A rejected renderer shows the original code with an error label. Clicking the preview exposes editable source; leaving the block renders it again. Incomplete fences stay source. Async results are discarded after the preview is destroyed, and the view measures the completed result. `codePreviewPlugin(renderers)` is available for standalone composition. ## Tables, save and external edits Cell previews render inline links (including relative paths and autolinks), nested bold/italic text, strikethrough, highlights and code spans. URLs, escapes and code use the Markdown parser so punctuation is not mistaken for formatting. Reference-style links, Wiki links, images and math inside cells remain source text. Click a cell to edit its Markdown; leaving it restores the preview. Ctrl/Cmd-click a preview link to open it in a new tab, or use an ordinary click in reading mode. Relative links resolve against the host page URL. Table cells expose their Markdown on focus. Enter and Tab commit and move to the next cell in the corresponding direction; Escape commits and returns focus to the editor. Tab past the final cell appends a row. Toolbar actions commit drafts before changing table structure. Multiline/tab-separated paste fills a grid and grows it if needed. The toolbar and row/column handles appear on hover or focus. Drag a handle to reorder, or focus it and use Alt + arrow keys. Reordering preserves column alignment and participates in undo. The editable `.cm-table-cell` is a child of `th`/`td`; handles are siblings outside editable text. Host CSS should not assume the table cell itself is contenteditable. Cell drafts commit on blur, navigation or a structural action; they are not dispatched on every keystroke. Before a programmatic export/document replacement, blur an active `.cm-table-cell` inside your editor so its synchronous commit reaches `view.state.doc`. Browser saving in the demo observes committed transactions. Simultaneous external replacement of a table while a cell has an uncommitted draft is not a collaborative editing protocol; coordinate those operations in the host. ## Outline and large documents `getDocumentOutline(state)` returns `{ from, to, level, text }[]` from the currently available syntax tree. Recalculate after text edits **and syntax-tree changes**, since CodeMirror can finish parsing asynchronously. Use `ensureSyntaxTree` from CodeMirror if a synchronous export requires the full outline. Inline decoration plugins use visible ranges. Block decorations remain state-backed because they affect layout. Code highlighting uses a bounded cache; fences above 100,000 characters fall back to plain text, and auto-detection is limited to 20,000 characters. The browser benchmark includes the demo's outline and localStorage listeners; it is not an editor-only microbenchmark or a guarantee for every device.