Skip to content
QuireKit MSFE
Developer docs
v0.0.0 · Experimental

MD Editor component

Integrate a Markdown document editor that shares QuireKit Prose styles.

Try a complete document

Open the standalone MD Editor example. Edit one document, inspect its source, switch to read-only mode, or capture a static MdProse reading view. The example includes nested containers, disclosures, tasks, lists, quotes, code, tables, formulas, flowcharts, mind maps, definition lists and footnotes. Switching modes retains the editor, selection and undo history.

@quirekit/md-editor combines MSFE features with public QuireKit components. MSFE owns source, transactions and parsing; Prose owns document styling. Use Vue integration when you want to assemble the entire interface yourself.

Choose the saved model first

NeedChoice and boundary
README files, technical writing, Git review and portable MarkdownMD Editor keeps source authoritative; visual and source views share text and history
Rich documents, visual blocks, richer tables and formattingBlock Editor uses a PM document and product interface; check delivered capabilities, and treat Markdown export as a conversion that can lose structure
Custom schema, node views or an existing Tiptap ecosystemNative Tiptap supplies the framework and extensions; the host assembles UI and services, and schema defines supported structures

GFM cells support bold, italic, strike, links and inline code. They cannot contain nested tables, lists, code blocks or multiple paragraphs, and do not express merged cells or arbitrary widths. Inline styles can nest, but surrounding punctuation and text determine delimiter parsing. Code spans are literal. Unimplemented partial-style splitting is not a Markdown ban on combinations.

Visual format commands share semantic validation: preserve existing formats and prove the requested format, or refuse without changing source or adding spaces. Cells permit inline formats while headings, lists and block insertion are disabled there. Source input remains free, including incomplete and custom syntax; unknown structures retain source fallback. Extensions need compatible readers. Saving only .md does not include external comment or collaboration state.

The fixed default document contains the full comparison and editable examples. References: GFM tables, CommonMark emphasis, Tiptap schema.

Four views

Desktop code language uses a compact dropdown with arrow-key navigation and label-prefix matching; text style uses an anchored picker. Narrow code language retains a searchable bottom picker, while text style joins the bottom formatting task. Existing custom languages remain available; choosing Plain text clears the fence's language marker. A code language choice applies immediately and can be undone once; cancel preserves source. Formatting buttons also apply immediately, and closing the panel does not undo accepted settings. The narrow example page also uses a picker for document views, preserving selection and history when editing resumes.

Below 768px, the text column uses compact spacing and the bottom toolbar provides undo, redo, insertion, text style, lists and import/export. The dock uses the public Toolbar mobile mode without Tooltips. Contextual table commands move into More only when they do not fit; More opens a bottom action surface. The insertion directory places common content before diagrams, structure and text conversion, with prompts for search and parameter fields. Text formatting offers Select all and Select current block; the latter selects the nearest complete Markdown block at the caret. Empty lines and incomplete fences do not offer block selection. Selecting a range changes neither source nor history. Settings retain a static caret or range after previously focused text loses focus. Document replacement and read-only/locked mode clear or hide the static caret. Mobile live mode keeps disabled custom endpoints when the keyboard closes or the window loses focus; tapping an existing selection toggles actions without refocusing or reopening the keyboard.

The selection menu uses the public Toolbar for Copy, Cut, Paste and Select all, and closes after an action. Paste reads plain text from the system clipboard. If the browser denies access or the page uses LAN HTTP, a paste panel opens: use the system Paste command in the Text to paste field, then choose Paste to replace the original selection. Cancel and empty text leave the document unchanged. Paste and Cut each take one undo step. Changing the selection or document while a read is pending cancels that request.

On narrow touch devices, focusing editable text shows the public SelectionHandles caret grip. Drag it to move the caret. Holding text or the grip preserves an insertion point, without preselecting a word, until actual dragging extends a selection. After release, either endpoint can be dragged across lines or past the other endpoint without changing its identity. Selection movement does not add text undo entries. In mobile live mode, the rendered text is noneditable; an independent input proxy and shared @quirekit/ime receive keyboard and composition input, while the product draws the caret and selection to avoid overlapping native menus and selection. Preedit appears temporarily in the document without changing source; confirmation commits one CodeMirror transaction with one undo step. The caret stays visible during composition, with dragging disabled. Source view, desktop, read-only state and embedded editors retain their own input behavior. MSFE/CM always owns the document, logical selection, commands and history. Hidden syntax and zero-height gaps accept only visible caret boundaries on either side. Tapping or dragging onto a table divider places the caret at an adjacent cell's text insertion point. Empty cells remain editable, and composition at a cell boundary stays inside the corresponding cell. Dragging at the usable viewport edge scrolls automatically and stops on release. When the keyboard reduces that viewport, the caret and grip reveal above the dock. Already visible typing does not scroll repeatedly, and manual scrolling does not pull back to the previous position. The dock follows the visual viewport without duplicating safe-area padding when obscured. The example editor route disables pinch and keeps native input at least 16px to reduce focus zoom. Native iOS accessory bars and candidate windows still require physical-device verification.

MD Editor has four views. All of them edit and display the same Markdown source, with no format conversion in between:

ViewSettingBehavior
Document (default)view="document"Recognized markers stay hidden while editing. Tables retain cells, code retains controls, and containers retain their nested structure
Live previewview="preview"Paragraph or list-item markers reveal together. Active tables show structural source and selected cells show inline markers; container fences share the same indentation
Sourceview="source"Inspect and edit all source
Readingview="reading"Renders the current document with components; not editable. The editor stays mounted, so selection and history survive switching back

Read-only is readOnly over the first three views rather than a view of its own: it only stops input, so links, folds and copying keep working, and it persists across view switches. All four views share Markdown, selection, history and Prose styling. The example lists all four views and a read-only switch; your host may offer any subset.

vue

A framework-neutral instance has the three editable views (reading is rendered by the Vue component). Switch them with the public methods:

ts

Switching does not mutate source or enter undo history. Requests defer during pointer selection and composition to preserve the input DOM. Hidden markers participate in native atomic navigation; Enter continues lists, and Backspace at a list start can remove its marker. Unknown or unfinished syntax stays editable source. Custom projections follow the source reveal contract. This does not automatically give every Markdown plugin an editable document UI: complex nodes still require explicit projections, commands and fallback behavior.

Live preview reveals semantic owners together: a multi-line paragraph shows its bold, emphasis, link and inline-code spelling as one unit. A task item's list marker and checkbox source appear together, while sibling items stay rendered. Nested lists resolve each item independently, and container paragraphs retain local inline activation. An active table shows its pipes and delimiter row, but only selected cells reveal inline markers. IME preedit follows the same ownership; unrelated headings, tables, code, math and diagrams remain projected.

Enter and writing after blocks

A soft rounded background identifies the paragraph, list or quote containing the caret. Inside a container it follows the inner block. This passive hint does not change source, add spacing or intercept the pointer. Source view, read-only state and Reading view do not show it.

Both editing experiences preserve newly created empty paragraphs. Compressed structural separators between blocks do not accept the text caret; navigation lands in neighboring real content. Source mode can edit those lines, and returning to editing classifies the result from the new syntax. In paragraphs and ATX headings, including paragraphs inside nested containers, Enter inserts a Markdown paragraph separator, splitting the paragraph when used in the middle. Empty documents, repeated Enter, and exiting an empty list item remain editable. Prose still owns structural spacing; these edits do not serialize the whole document.

Position or keyBehavior
Enter in a paragraph or ATX headingSplit at the caret or start a paragraph at the end; text after a heading uses the body style
Enter in a list or quoteContinue the current marker; an empty list item or quote line immediately exits one level, retaining a paragraph separator when returning to body text
Enter in code contentAdd a line inside the code block
Enter in a document-editing table cellInsert a row after the current row, retaining columns and alignment; Tab / Shift+Tab still navigate cells
Cmd+Enter / Ctrl+EnterStart a paragraph after the current code block, container, table, list or quote; nested blocks retain outer quote and list indentation
Shift+Enter in proseInsert a Markdown hard break using two trailing spaces and a newline; preserve outer list indentation and quote prefixes without inserting <br>

These paragraph rules apply to both document and markdown; source mode keeps native source input. At a quote's end, the first Enter immediately shows an editable blank line inside the quote. A second Enter exits the current quote level; nested quotes exit one level at a time. Code content keeps literal newlines, and Markdown-editing tables keep source operations. At a Setext heading's end, the new paragraph starts after its underline. Paragraph insertion is individually undoable, and the same editor continues to own read-only and composition state.

Caret and editing boundaries

In document mode, the text caret enters editable content. Features declare the complete source range and its editable interior; the shared runtime uses them for arrows, Home/End, clicks, selection drags, deletion and replacement across boundaries. Every position is a Markdown source offset. Hidden markers and compressed gaps do not introduce another line numbering system or document.

Inline code retains separate outside and inside insertion positions by default. Bold, emphasis and highlights enter their content directly. Code fences and language metadata are protected, while the code body stays editable. Partial cross-block deletion retains the shell; selecting a complete block allows its removal. Projected block components such as properties and diagrams are whole objects: deletion at an edge first selects the object. Enter, explicit line breaks and block exit still use addBlockInput(); see Block-owned Enter.

Override product defaults

editingBoundaries overrides rules by parser node name. This example makes inline code enter its content directly. Set navigation to split to restore separate outside and inside stops. Prepare the host's #editor element before mounting.

ts

nodes names parser nodes. from/to includes the backticks and text, while contentFrom/contentTo covers only the code content. deletion: 'unwrap' removes paired markers at the relevant edge. The query does not dispatch transactions or change caret pixels. Call editor.destroy() when the host leaves.

Vue takes the same inlineCodeRule through the component prop:

vue

Rules are read on creation/mount. Recreate the instance when changing the rule set and preserve the required document in the host. editingBoundaries: [] adds no host overrides, so built-in rules remain active. The first rule at the highest priority owns each node. Returning null opts that syntax owner out without falling back to its default rule. Projected components retain their independent whole-object protection.

Declare rules in a feature

Rules belonging to a feature go in addEditingBoundaries(). For the existing inline-code grammar, reuse the rule above in an extension and install it through extensions: [InlineCodeBoundaries]:

ts

With feature installation, let the extension own the rule and reserve the product's editingBoundaries for host overrides. Existing features can return rules in their own hook or combine parent rules through .extend(). Put overrides before parent rules for the same node. The hook only describes boundaries: a new syntax still needs parsing and projection registration.

when defaults to hidden, enabling the rule when presentation hides syntax globally. prose also queries rules in Live preview; use isSourceRevealed(state, node) to check whether that particular structure is already revealed. Source view disables boundary constraints. If text is entered on a source separator, returning to document mode classifies it as content from the new syntax.

Currently navigation: 'native' opts out of the entire syntax boundary rule. Use deletion: 'native' to delegate editing while retaining navigation constraints. The older inlineBoundaries option uses the same runtime and replaces only its original inline rule list. See Editing boundaries for the six kinds, complete fields and advanced CM integration.

Heading folding

Headings with section content have a disclosure arrow outside the text column, without reducing title width. Expanded sections show it on hover or keyboard focus; collapsed sections and touch devices keep it visible. Collapsing keeps the title and hides content up to the next heading of equal or higher rank. Reopening a parent preserves each child heading's fold choice. ATX and Setext headings are supported; sections inside containers, quotes or list items stay within that container.

Document view, Live preview, read-only and Source view share the same fold state. Folding does not modify Markdown or add text undo entries. Finding hidden content reveals it; a heading-level change that invalidates an existing range also unfolds it. Fold choices are not saved in the file and reset on reload or document replacement. Static Reading view continues to show the full document.

The capability belongs to @quirekit/msfe-heading. Base MSFE integrations can use Heading.configure({ folding: false }) to disable heading controls and section validation while keeping CodeMirror's existing source folding. Hosts can supply controls and toggleLabel for their own components and labels; MD Editor uses the public QuireIconButton and shared icons by default.

Block handles and menus

Hover a block to reveal a neutral gray, bordered type-and-grip button outside the heading disclosure arrow. Click to open actions, or press Cmd+Alt+B / Ctrl+Alt+B in the document. The menu uses public QuireKit components for paragraph, heading and list conversion, adjacent insertion, copying Markdown, cutting, duplication and deletion. Only valid conversions appear: lists offer list-type changes, while code, tables and formulas omit the conversion section to preserve their structure. Narrow touch devices hide the desktop hover grip while keeping insertion in the toolbar; an already open block menu retains its anchor.

Adjacent insertion groups all installed headings, Markdown blocks, formulas, flowcharts, mind maps, whiteboards, text diagrams, dates, emoji and host contacts inside the public QuireScrollArea. Images, links, formulas and diagrams open their parameter editor first and insert once before or after the captured block on confirmation. Cancellation leaves no blank block. Changes to the original block, document replacement or read-only permissions reject obsolete submissions.

The default document includes a project delivery flowchart, project plan mind map and idea whiteboard, with complete editable data and PNG previews saved from that same data. Double-click to continue editing. The text diagram section also includes a PlantUML sequence and a Graphviz / DOT delivery graph; like formulas, a click selects them and a double-click opens the editor, with preview and SVG download in the action bar. Reading view shows the same document.

“Flowchart”, “Mind map” and “Whiteboard” open the shared Flow Editor, Mind Editor and Whiteboard Editor. “Text diagram” opens the shared source workbench with Mermaid, PlantUML and Graphviz / DOT. Mermaid flowcharts, mind maps, sequence diagrams and others are written from templates in Text diagram and saved as Mermaid code blocks. Flowcharts and mind maps edited by dragging retain their own editable data. The toolbar, empty-line plus and / menu share these entries, with diagrams grouped before paragraph blocks. Clicking a flowchart, mind map or whiteboard selects it and shows an action bar to edit, preview, download the PNG, copy, cut or delete it; whiteboards can also be renamed. Press Delete to remove the selection or Esc to release it. Preview gathers the document's images and these diagrams into one gallery in document order, so you can page between them.

Focusing or hovering an editable empty line shows a gray outlined plus on its left. It opens the same insertion catalog directly. Selecting content replaces that line while retaining required paragraph separators, with one undo step. Cancelling the menu or parameter editor leaves the source unchanged. Whitespace inside code, formulas or lists is not a standalone insertion point; containers with declared child flows support insertion on their empty lines.

Type / in an empty paragraph to open the same catalog, then search by localized name, English alias, or Chinese pinyin initials: /table, /bg, and /h2 are examples. Use Up/Down to select, Enter to insert, and Escape to close while keeping the literal query. Images, formulas and other parameterized entries reuse the existing editors: cancellation keeps /query, and one undo after confirmation restores it. Native editing focus stays in the document, including Chinese input. Ordinary prose, URLs, code, quotes and list interiors do not trigger the menu. This entry is independent of block handles and enabled by default; :slash-menu="false" disables it dynamically.

Dragging displays a source summary and insertion line; release moves the source in one undo step. Lists, quotes, code, tables, formulas and containers move as complete blocks. Direct children of a container can move within that same container. Cross-container moves and individual list-item dragging are currently excluded; insertions that would break the outer fence are unavailable. Escape or loss of window focus cancels a drag. A source change closes obsolete menus. Source view, read-only state and Reading view hide the controls. Click inline formulas directly to edit them; they no longer have an extra pencil button.

Vue enables the controls by default; pass :block-controls="false" at mount to disable them. Custom hosts can use createSourceBlockControls from @quirekit/msfe-core for current block, client-space geometry, drag state and revision-guarded command targets, while rendering their own UI. Custom nested syntax declares child flows through sourceBlockScopes. Optional insertionChange and openInsertionMenu callbacks provide SourceInsertionControls for empty lines. sourceBlankLineAt queries the current position and insertAtSourceBlankLine performs guarded insertion without inventing a syntax block. rect covers the complete block; firstLine describes its first visible native text row so controls can align with the first row of a wrapped heading. Opaque block widgets return null, allowing the host to fall back to the block's top edge. See block controls and source block commands.

Each MdEditor owns a document overlay for block controls, block menus, mentions and toolbar insertion panels. Descendant components can call useMdOverlayTarget() from @quirekit/md-editor/vue and pass the returned readonly ref to the public Menu's portalTarget; submenus inherit the same target. Menu still owns positioning, viewport constraints and dismissal, while modal editors use the public Dialog. This API supplies the mounting container, not block commands or context-menu business behavior.

Dependencies and host setup

Use workspace packages or locally built artifacts. A stable public npm release is not promised yet.

sh

Reuse the application's QuireKit theme, icon provider and UnoCSS or standalone stylesheet. The product does not ship a second document skin. See UI and themes. Mount MdEditor on the client in SSR applications, for example inside Nuxt's ClientOnly.

Also import @quirekit/md-editor/styles.css from the built package. This stylesheet owns toolbar and document layout only; public Prose styles still own document typography and block appearance.

The editor uses source-maintained @quirekit/cm-* and @quirekit/lezer-markdown packages. Block-gap measurement, retired view cleanup and syntax-reuse observations ship in their builds; consumers do not need separate CodeMirror/Lezer installation patches. Each package includes UPSTREAM.json with the upstream version, commit and local differences, plus the original MIT license. Native CodeMirror extensions must import from the same @quirekit/cm-* family. Adapt third-party extensions that depend on official @codemirror/* packages before using them, so one editor never mixes different State, View or Facet identities.

Mermaid uses [email protected] with patches/[email protected] to correct shifted mind-map node labels in SVG-text mode. Editing, reading and Word image export share this fix. External consumers still need this Mermaid patch; it targets the default ESM entry, not standalone browser scripts.

Mount, edit and save

vue

markdown is the initial value, read once on mount. It is not a two-way model. change emits the original incremental MsfeChangeEvent, without copying the entire document on each keystroke. Use the exposed editor's replaceDocument() for a different document and applyChanges() for versioned external updates. See Reading and saving.

Errors and source preservation

MdEditor uses the public QuireAlert for creation and runtime errors, with messages matching locale. Failed creation displays the initial Markdown in a copyable QuireProseSource instead of leaving an empty editor. Errors from extensions or listeners in a mounted instance preserve its document, selection and undo history. Dismissing the alert does not recreate the instance. Save the source before the application remounts the editor after fixing its configuration.

Use @error="handleEditorError" to receive the original unknown exception for your application's diagnostics. Internal codes and stacks are not used as user-facing messages. This covers editor creation and errors reported through MSFE's onError; it does not suppress unrelated Vue errors or build-tool diagnostics.

Configure the product

PropertyPurpose
localezh-CN or en for controls; never translates document content
labelAccessible name of the editing region
viewdocument (default), preview, source or reading; all four share source and history and update reactively
readOnlyRead-only over the first three views; disclosures and copying remain available
toolbarBuilt-in toolbar: true or 'fixed' (default) stays above the document; 'floating' shows text style, inline formats and link above a desktop text selection; false hides it. Narrow screens use the bottom dock whenever it is not false
selectionToolbarWith the fixed toolbar, a desktop text selection also floats style, inline formats, link and Add comment above it; on by default. A read-only source keeps only Add comment
blockControlsEnable block handles, menus and drag sorting at mount; enabled by default
slashMenuSearch and insert with / in empty paragraphs; enabled by default and dynamically configurable
placeholderHint shown in an empty editable document; defaults to a localized "Type / to insert" prompt, an empty string hides it; read on mount
mentionsHost contact array or cancellable async provider; enables the contact picker and typed @ trigger when supplied on mount
documentLinksHost document links: search is a document array or cancellable async search (an empty query may return recent documents) — typing [[ opens a document search that inserts a standard [title](url) link, and the link dialog can search documents too; recognize identifies host document URLs, which render as references with a document icon; preview returns a Markdown excerpt shown as a thumbnail when hovering a reference; exportUrl sets the destination written when exporting Markdown, Word and other files, such as an absolute URL
docxWord converter, conversion and image options such as { converter: mdDocx() }; omitted or false hides Word import and export
exportersHost-installed export formats (MdExporter list), such as PDF and OFD; each adds an Export menu item and reuses the selected template and node converters
layoutembedded for a framed component; document for a full-width toolbar and centered document
outlineHeading outline that follows the reading position in the document layout, off by default; with room it is pinned to the editor edge and can be collapsed to a button and expanded again, otherwise the button opens a popover; the document centers independently and makes room for the outline and comments when space is limited
outlinePositionOutline side: start (default, left of the text, leaving the right side to comments) or end (right of the text, after the comment rail)
featuresReplace or disable features through StarterKit options
containersContainer options, or false; types replaces the complete name list
extrasReplace or disable math, mermaid, textDiagram, footnotes, definitionList, subscript, superscript, insertedText, highlight and frontmatter; false disables all extras
extensionsAdditional MSFE features; do not install duplicate built-in names
codeLanguagesBuilt-in lazy catalog by default; supplying a catalog replaces it, [] disables it, and unregistered languages stay plain text
editingBoundariesOverride or add boundary rules by syntax node at mount; [] retains feature defaults. See Caret and editing boundaries
inlineBoundariesCompatible inline rule list; defaults to separate code positions and direct content entry for other styles; [] removes this list while link, block and component rules remain active

Features and language catalogs are installed on mount. Explicitly remount for a different feature set and preserve the document in the host. view and readOnly update directly. Key the component by locale when switching the complete interface language.

The policy accepts InlineBoundaryRule[]. Each rule declares syntax nodes, opening/closing marker children and a behavior: split preserves distinct inside/outside insertion positions, content skips duplicate boundary stops, and native opts that inline rule out of boundary handling. Import and copy mdInlineBoundaries from @quirekit/md-editor to customize the defaults. The Vue component and createMdEditor share this option. It applies to hidden syntax in document mode; source reveal, drag selection and composition retain their existing behavior. This option and the lower-level inlineBoundaryNavigation(rules) now adapt to the shared editing-boundary runtime; prefer addEditingBoundaries() for new features. See Custom code blocks for empty-code placeholders and default languages.

The built-in toolbar uses public QuireToolbar, sharing Classic's grouping, tooltips, toggle states, arrow-key navigation and narrow-screen scrolling. Menus capture the selection before opening and mutate the same Markdown through its existing command and undo contracts.

FeatureSource and behavior
Paragraphs and headingsParagraphs and six heading levels over the original Markdown
Inline formattingBold, italic, strikethrough, inline code, plus ++underline++, ==highlight==, H~2~O and x^2^ extensions
ListsConvert the current paragraph or selected lines to bullet, numbered or task lists; indent nested lists, continue with Enter and exit with Backspace
Links and imagesEdit destinations, labels and titles in a shared dialog; images use QuireProseImage, without embedding resource contents in the document
TablesInsert above/below or delete the current row; insert left/right or delete the current column; set left/center/right/default column alignment
Code and containersCode language and copy controls; notes, warnings, disclosures, highlight blocks and nested containers with directly editable bodies
FormulasInline $…$ and display $$…$$, edited with the shared structured formula editor
Text diagramsShared Mermaid, PlantUML and Graphviz/DOT workspace with templates, live previews and SVG/PNG downloads
Notes and terminologyAtomic footnote reference/definition insertion, navigation between them, and definition lists
Dividers, dates and emojiStandard thematic breaks, plain ISO calendar dates and Unicode emoji; emoji use the public QuireEmojiPicker
MentionsChoose host contacts from Insert or type @ at a word boundary; writes an ordinary [@name](<destination>) link without sending notifications
FilesPreview Markdown or Word imports, then replace the document or append to its end; export Markdown, Word or host-enabled PDF/OFD

Underline, highlights, formulas, footnotes and custom containers are extensions; other applications need compatible plugins to render them. GFM tables have no merged-cell syntax. Merging cells, column widths, font sizes and text colors are not presented as standard Markdown capabilities. Cloud documents, comments and polls are outside these source commands.

In document layout, --md-editor-toolbar-top reserves space for a fixed host header (default 0px), and --md-editor-document-width controls the document's maximum width (default 960px). When the toolbar sticks inside the host's own scroller, --md-editor-panel-top places the side panel below the host chrome (defaults to the toolbar offset). For a host toolbar, set :toolbar="false" and invoke public commands through product.editor. Do not replace the editable DOM. Host menus can call the instance's openFile(mode, exporter?) to open the same windows as the built-in Import / Export menu: mode is markdownImport, wordImport, wordExport or formatExport (the last names an installed exporter such as pdf or ofd). It is ignored while another task is open or when the action is not enabled.

The header slot places host content above the document, aligned to the document width, such as a title input or author details; the footer slot does the same below the document, such as likes or a comment entry. Both scroll with the document, are not editable content and are never written to Markdown.

vue

Pass ProseCodeBlock.extend({ addNodeView() { … } }) through features.codeBlock to replace code controls while retaining the Prose frame and source language command. Replace default formula and diagram features through extras.math / extras.mermaid / extras.textDiagram, not duplicate entries in extensions. Add new diagram languages such as ECharts through extensions. See Code blocks, Diagrams and Markdown plugins.

Short identifiers such as issue keys can appear as links without changing the source: textLinks() from @quirekit/msfe-link matches visible text with a host rule and is installed through extensions. The saved Markdown keeps the original text, such as CLOUD-15084.

ts

Matches look like ordinary links and carry data-msfe-text-link; Cmd/Ctrl+click follows them while editing and a plain click does in read-only mode. Code, existing links, images, HTML and math are never matched, nor are matches touching letters or digits; source view stays raw. Failed checks are not cached and are retried. An unchecked candidate ending at the caret waits until the caret leaves, so typing does not query every prefix. Pass the same options object to several editors to share results. With card, hovering a link for 300 ms shows a card: subtitle, title, up to three lines of description, a status tag (tone is neutral, info, success, warning or danger), short fields and an open-link action; returning null shows none. Add fromHref(url) to map destinations back to text so ordinary Markdown links to the same target, such as [spec](https://issues.example.com/browse/CLOUD-9), show the card too. With exportLinks: true, Markdown, Word, PDF and OFD exports write matches as standard links that work in other software, while the editor source stays unchanged; host-owned exports can call linkifyTextLinks(editor.state) for the same result. Cards are cached by text and failed requests retry on the next hover. Converting matches to real links on export belongs to the host.

Comments

Comments come from the commercial package @quirekit/md-editor-comments (free for development and evaluation; production use requires a license key): pass mdComments() to comments and import its styles.css. Select text and use Add comment in the toolbar, the floating selection toolbar or ⌘/Ctrl + Alt + M. People can reply, edit or delete their own messages and resolve threads. The focused card has previous / next buttons with its position, the toolbar Comments menu also moves between comments or shows them all, and resolving a thread moves on to the next unresolved one. Comments never enter the Markdown: the source stays clean, and the application stores comments itself in a standard exchange format.

vue

Wide document layouts show comments in the right margin, each card aligned with its text; narrow screens, embedded layouts and phones open a list from Comments. With mentions, typing @ in a comment field lists contacts and inserts the same [@name](<url>) link as the document, also listed in the message's mentions for notifications. Comment text supports bold, italic, inline code, strikethrough, links and mentions; other Markdown shows as written.

canComment: false allows viewing only; canResolve decides whether Resolve / Reopen is offered and is unrestricted when omitted. Users can always delete their own messages; canDelete also lets roles such as moderators delete other people's messages (never edit them). avatar returns an avatar URL per author, falling back to initials. These settings only shape the interface, so the application's server must enforce the same rules. Read-only documents can still be commented. Comments are not exported to Word, PDF or OFD, and Word comments are not imported as comments. The exchange format, re-anchoring and collaboration plans are documented in the types of @quirekit/msfe-comments.

Besides comments on selected text, people can leave document comments on the whole document from Comment on document in the toolbar Comments menu or the comment list. They have no position in the text; replies, resolving and mentions work the same, and they are grouped as Document comments at the top of All comments. With documentComments: "end", a document comment section also appears after the document. Data with document comments exports as exchange version 2; data without them stays version 1.

To exchange files with tools that support CriticMarkup, set criticMarkup: true. Export gains “Export Markdown with comments”, which writes comments as {==text==}{>>@author: comment<<}, and importing Markdown turns those marks into comments without keeping them in the text. Inside the editor the source and the comments stay separate; marks exist only in the exported file. Each direction can be configured:

ts

suggestions decides whether {++added++}, {--deleted--} and {~~old~>new~~} marks are kept as written, all accepted or all rejected on import. Comments in code or across lines are written as a separate mark beside it, so existing syntax is never broken. Without the editor, call exportCriticMarkup and importCriticMarkup from @quirekit/msfe-comments/critic-markup directly.

AI assistant

createMdEditorAi() from @quirekit/md-editor-ai returns panel, extensions, and destroy(). Pass the first two to MdEditor and call destroy() when the host page unmounts. The assistant uses built-in model settings, document tools, and change review. ownerDocument supplies the page document, storage supplies browser storage, and images can reuse the host's image storage.

For centrally managed models, pass connection: () => Promise<QuireAiModelConnection> to load authorized models when the panel first opens. This hides personal connection settings and bypasses saved provider configuration; model selection and approval preferences can still persist. An administrator page can reuse createQuireAiProviderSettings({ persistence }) with initialDraft, asynchronous save(draft), and clear() backed by the host server. Failed saves retain the draft without writing browser credentials. Use @quirekit/ai-assistant/settings for server validation; the host owns credentials, member permissions, and gateway authentication.

Activity

Activity answers who did what, and when. Records live outside the document in the public @quirekit/edit-history protocol; the host implements ActivityStore, and the editor only records and displays. Install both parts with the same store and document identity:

ts
vue
  • Typing in one block becomes one entry after a pause of about three seconds, a move to another block or a blur; paste, cut, undo, block insertion, moving or deleting blocks, conversions and highlight color or icon changes each make one entry. Changes written by peers or the host are not recorded locally.
  • The panel groups entries by day and nearby time, expands details, shows the Markdown before and after, filters by member or time and hides system notices. Clicking an entry selects the current source: when the place moved it selects the same content and says so; when the content was deleted or is ambiguous (the same text appears more than once) it only says so instead of jumping elsewhere. Comment entries select the text the comment currently marks.
  • For single-user use set trust to local; local: true marks the panel as device-only. Verified multi-user records need server confirmation; a general collaboration collector is not provided yet. Hosts can write protocol events after accepting source updates on the server and mount only mdActivityPanel on the client to avoid duplicate local recording. The in-memory store in @quirekit/edit-history/memory is for demos and tests and is lost on reload; check your own store against the rules in @quirekit/edit-history/conformance.

With collaboration

In collaboration the server confirms and writes records; clients no longer write the store. Install the Hocuspocus extension on the server; authors come from authentication:

ts

Clients send hints only for labelled operations such as block insertion, paste and undo; the server merges typing from the updates it receives:

ts

When a hint does not match the observed change, the server records the change as a plain edit. A quick reconnect continues the same session instead of recording another join. With commentsRoom: room => room.endsWith(':comments'), adding, replying to, resolving, reopening and deleting comments are recorded too, authored by the connection; take the comment room's records with takeActivity(commentRoom) when storing that room.

Server records need a cap or they keep growing. The protocol's retention policy limits both count and age: maxEvents keeps only the newest entries per document, fullDays drops changes and keeps summaries, and summaryDays removes entries entirely. The reference createMemoryActivityStore({ retention }) applies it after every append; a database store can run the same cleanup after its write transaction or on a schedule, for example deleting old rows past the count by sequence. Versions are thinned separately with versionThinning; named versions are kept.

Versions

A version is a snapshot of the whole Markdown, stored by the host's VersionStore. Give the same version store to MdActivity and the panel, and the panel gains a Versions tab:

ts
  • Type a name to save a named version, which is kept permanently. The host calls getMdVersions(editor).save() at its own save points for automatic versions; when nothing changed since the latest version it is reused, not stored again.
  • Clicking a version opens a read-only preview; "Compare with current" shows a line diff, and "Copy all" lets you paste back the parts you need by hand.
  • "Restore this version" saves the current content as a version first, then rewrites only the differing lines as one undoable edit recorded as "Restored a version"; comments and collaboration anchors on unchanged lines survive. Read-only editors cannot restore.
  • In collaboration the server takes automatic versions while storing, and the host's version API records "Saved a version" on the server with the authenticated author; restores are confirmed through hint pairing as usual, and the client never writes records itself. Undoing one past edit on its own is not offered yet.

Use the shared formula editor

Choose an inline or block formula from Insert. Double-click an existing block formula (or select it and choose Edit in its action bar), or click an inline formula, to edit it. The dialog uses MathAuthoringPanel from @quirekit/math-ui, retaining the @quirekit/math document, templates, structured input and optional LaTeX entry. The product does not implement another formula interface. KaTeX rendering resources and the authoring panel load on demand.

Drafts are separate from the document. Cancel makes no change. Confirm replaces only the formula's LaTeX body, retaining dollar fences and surrounding quote/list prefixes, as one undo step. Inline formulas require a single line without surrounding dollar signs; use display formulas for multiple lines. If the anchored content changes, the document is replaced, or editing becomes read-only, submission is refused and the draft remains available.

Workspace hosts using UnoCSS must scan the public math-ui sources alongside product and UI sources. The panel uses existing public component classes. Hosts using precompiled styles must include the corresponding public component styles.

For a custom formula host, configure MSFE's Math.configure({ edit }) and pass it through extras.math. The callback receives editor, range and kind; return true after taking ownership. Do not retain a bare range across an asynchronous dialog: call captureAnchor(range), obtain anchor.target() on confirmation, run editor.run(replaceMdPreview(latex), target), and release the anchor on close. The same contract applies to Mermaid.configure({ edit }). See the rendering API for component lifetimes.

“Insert → Text diagram” opens the shared Classic, Block and MSFE source workspace for Mermaid, PlantUML and Graphviz/DOT. Switching languages retains separate drafts; applying a template is explicit. Successful previews can be saved or downloaded as SVG/PNG. Errors retain the draft, and cancellation leaves the document unchanged. Standard Markdown fences have no separate title field, so this host hides title editing.

Saves produce ordinary mermaid, plantuml or dot fences; puml / graphviz aliases can also be reopened. Editing preserves fence style and enclosing block prefixes, growing delimiters when needed to prevent source from closing the fence. The language name changes only when switching grammar. extras.mermaid: false disables Mermaid; extras.textDiagram: false disables PlantUML/DOT. Disabled languages remain ordinary code. Engines run locally; external includes, optional PlantUML libraries and D2 are not enabled. See text diagram integration.

Place the caret inside a link and select Link in the toolbar to edit it. Clicking an image selects it and shows an action bar to edit, preview, copy, cut or delete it; double-click previews it directly. Unchanged labels preserve their original formatting. Editing one reference-style link converts that usage to an inline link while retaining the shared definition and other usages. Unsafe destination protocols are rejected.

Markdown stores an image destination. The host owns uploads, private attachments and object-URL resolution. Use Image.configure({ resolve }) to retain the image resource contract and override addNodeView for a custom public image component. MSFE still owns resource release through node lifetimes; see Images and resources.

With images (MdImageStorage), the host's storage receives new images and the document stores only the returned address:

ts
  • The image dialog shows an upload action that fills the address and alternative text for confirmation.
  • Pasted screenshots and dropped image files upload directly and are inserted where they were pasted or dropped; edits during the upload do not move the landing point. A clipboard that also carries text, such as a copied web page, uses the ordinary paste. A failed upload writes nothing; the host reports it in upload.
  • Flowchart, mind map and whiteboard previews upload with source: 'diagram' on save, and the embed stores only the address (preview.url). A failed upload keeps the preview inline without losing the edit. Word, PDF and OFD export fetch the preview back through resolve.
  • When docx.storeImage / docx.resolveImage are unset, Word import and export use the same storage.
  • Read-only editors never upload; without images the editor keeps accepting addresses only, and diagram previews stay inline as base64.

Call product source commands

The toolbar consumes public commands. Custom controls use the same editor and transaction:

ts

replaceMdPreview(body, language?) requires a confirmed formula or text-diagram target. The optional language switches an existing diagram fence; it does not create a new block. Table commands require a selection inside one table. Column alignment modifies only the corresponding delimiter field, preserving all other cell source. Inspect result.ok: read-only, stale and structurally inapplicable targets refuse changes.

Do not apply a command planner by editing DOM or bypass the target, permission and history checks in editor.run(). External dialogs capture their intent and release anchors on completion. See Commands and transactions.

Import and export

The toolbar's up/down arrow icon opens Import / Export, with its name shown on hover. The menu groups actions by direction: import Markdown or Word, and export Markdown or Word. Formats installed through exporters, such as PDF and OFD, each add an export item; uninstalled formats are hidden. Each import and export has a file-type icon marked MD, W, PDF or OFD; disabled entries also dim their icons.

Import Markdown accepts pasted text or a local .md/.markdown/.txt file up to 2 MB. The shared FileUpload handles selection, validation and removal. Files load into a draft, strip a UTF-8 BOM, and normalize CRLF/CR to LF. The shared CodeBlock previews and copies the source; “Edit content” opens the editable draft for changes or pasted Markdown.

“Replace document” replaces all current content with the draft. “Append to end” keeps the original and appends the draft, independently of the current selection. Both actions undo in one step. Source changes, document replacement or read-only state while the dialog is open block writes and retain the draft. Hosts opening a different document and resetting identity and history should handle saving and call replaceDocument().

Export Markdown reads the authoritative current source, without converting Prose DOM or JSON back to Markdown. Backend persistence belongs to the host. Export also works in read-only mode.

Word import and export

Word actions in Import / Export come from a host-installed converter. mdDocx() lives in the commercial @quirekit/md-editor-export package; the base editor only defines the MdDocxConverter contract and hides the Word items when none is installed:

ts

The conversion runtime loads after an explicit user action. Import accepts .docx files up to 20 MB, converts to a Markdown draft and shows a CodeBlock preview and report. It shares the “Replace document” and “Append to end” actions with Markdown import; each undoes in one step. Cancellation, conversion failure, source changes during import or read-only state cannot write back. Export captures the source when the window opens; review the report before downloading. Read-only documents can still export.

The first slice supports paragraphs, headings 1–6, bold/italic/strikethrough/inline code, links, code languages, basic nested lists, bordered tables with shaded headers, native footnotes, equations and PNG/JPEG images. Code blocks retain language and line breaks, with shading and border spacing for extra inset space. Tasks become visible checkbox characters. Default containers and Mermaid use the node conversions below. On import, complex tables, fonts and layout are simplified; comments and headers/footers are omitted with diagnostics. Export templates can create new headers/footers. Word fidelity and original Markdown spelling are not guaranteed. Recognized tracked deletions are excluded and insertions retained; this is not a full Word “accept all revisions” implementation.

Inline and display equations use @quirekit/math-omml to produce editable native Word OMML. Reimport accepts LaTeX only after strict structural verification. Unsupported source equations retain their source; Word equations that cannot be decoded retain readable math text, with diagnostics. Complex formula properties, surrounding Markdown formatting and equations in notes are not guaranteed to round-trip.

Footnote references and bodies become native Word footnotes and import back as Markdown references and definitions, retaining basic paragraphs and text formatting such as bold and italic. Repeated references become independent Word footnotes with a diagnostic; unused definitions remain visible body notes. Note hyperlinks retain their labels and URLs, images retain alternative text, and complex blocks retain source with diagnostics. Word endnotes become Markdown footnotes with a placement diagnostic. Ordinary note labels are normalized, so their original spelling is not guaranteed.

Use the independent adapter without mounting an editor:

ts

markdownToLitAst() and litAstToMarkdown() expose the same intermediate conversion stages. The former returns authoredParts for styles and document settings. Custom compilation must pass them with the AST to createDocxPackagePlan(ast, { authoredParts }), so styles and round-trip metadata share one result. lossPolicy: 'strict' throws MarkdownDocxLossError with diagnostics when loss is detected; the default best-effort returns content and the report. The adapter is a QuireKit commercial component; existing MSFE licenses are unchanged.

There is no implicit image fetching. Export embeds PNG/JPEG data URIs; other sources require an explicit resolveImage(source): Promise<string | null> returning a supported data URI. Images retain their aspect ratio and shrink without enlargement to the actual body, list or nested cell area. The default template writes an A4 page with 1-inch margins. Headings default to 1.1 line spacing with 4–6 pt after, body paragraphs to 1.15 line spacing with 6 pt after, and list items to 2 pt after. Explicit spacing or style references supplied by custom conversions take precedence. Adjacent tables and framed containers receive an 8 pt separator in addition to existing paragraph spacing, including sibling blocks inside nested containers; reimport does not add empty content for this spacing. Export templates now configure these appearances; omitting a template retains the defaults. Import retains data URIs by default; storeImage({ dataUrl, alt }): Promise<string | null> can store them and return a persistent URL. Storage failure retains embedded data with a diagnostic; temporary blob: URLs are never persisted. Pass these options with the converter to the component's docx prop, or to a standalone conversion function. Omit docx or set :docx="false" to hide only the Word import and export items. Outside the editor (for example, when a host imports a Word file as a new document), call mdDocx().importDocx(data, { policy, features, signal }); it applies the editor's default conversions (flowcharts, mind maps, whiteboards, containers and code diagrams), so import and export round-trip consistently. Install custom node conversions with mdDocx({ extensions }). Markdown has no syntax for native Word charts, so import renders each chart from its cached data to a static PNG (stored through storeImage like other images) and reports one chart-rasterized per chart. Chart families it cannot read (scatter, combination, newer chart types and so on) become an [alt text] placeholder reported as chart-omitted; a chart never fails the whole import. Hosts can supply their own renderer with mdDocx({ renderChart }) ((chart) => Promise<string | null>); builtins: false disables chart rendering.

PDF and OFD export

PDF and OFD come from the optional commercial @quirekit/md-editor-export package as export plugins; the basic editor does not depend on it:

ts

Each plugin adds one Export menu item. Exporting captures the current source and selected template, then shows the physical page count and conversion report before download. Read-only documents can export. Changing the template regenerates the captured source; closing the dialog or switching documents discards stale results. Converters and fonts load on the first export.

Both formats paginate through a temporary Classic document, then encode native PDF or native OFD (without a PDF intermediate). Body text remains selectable. Tables, containers, footnotes, headers, footers and page fields use existing layout; diagrams become images. Equations currently use the default math face as high-resolution graphics, with a diagnostic, and their text cannot be selected individually. Custom nodes reuse the existing converters instead of a converter per format.

By default the plugins use @quirekit/font-pack's portable fonts: the same bytes are installed as browser faces for measurement and embedded in the file. OFD has no standard Courier, so the plugin embeds DejaVu Sans Mono for common code faces and reports the substitution; its license ships with font-pack. In PDF an unavailable code font resolves to a monospace fallback before measurement, with a diagnostic. Hosts can replace the whole font set through fonts and set title or author through metadata. Missing glyphs and resource failures are reported; lossPolicy: 'strict' rejects lossy output.

Without the editor UI, call exportMarkdownPdf(markdown, options) from @quirekit/md-editor-export/pdf or exportMarkdownOfd from /ofd; both return data, pageCount and diagnostics. Supply embeddable fonts explicitly. Non-browser hosts must provide textMeasurer and, for equations, rasterizeFormula. OFD fontEmbedding defaults to subset and can be full.

Neither format promises pixel identity with Word in different font environments, preserves editable Markdown metadata or supports Markdown import. PDF/A, Tagged PDF and background workers for large documents remain future work.

Export templates

@quirekit/export-template provides a format-neutral configuration, default style and strict validation under the MIT license. Templates affect export without changing Markdown content, selection, undo history or Word import. Page geometry uses mm; text sizes, spacing, borders and padding use pt. Line height explicitly distinguishes multiples from exact values. This is a public synthetic configuration example:

ts

Configure body text, headings H1–H6, lists, quotes, code, tables, containers, footnotes, equations and images. Font roles provide Latin, East Asian and complex-script slots; empty slots retain theme fonts. fonts.math.latin selects the native Word math font; equations remain editable. Fonts are not embedded, and pagination can vary between devices. Default single-section headers/footers support left, center and right content with {title}, {page} and {pages}. Page values are native Word fields that require reader updates; long single-line text is shortened with a diagnostic. The style illustration is not a Word pagination preview.

MdEditor accepts :docx="{ converter, template }" directly, or a host can install a template catalog:

  • templates: an ExportTemplateProvider with listing, exact revision reads and optional save/delete operations.
  • v-model:exportStyle: the current document's { id, revision }, persisted by the host alongside document settings.
  • #export-templates="{ provider, selected, choose, close }": mount the host picker; call choose to accept or close to cancel.

Providing both the provider and slot adds an “Export style” menu item. Word and installed PDF/OFD exporters reuse the selected revision. Changing the style inside the export dialog keeps its captured source and regenerates the file. Closing the dialog or switching documents discards stale results. Without host persistence, selection lasts only for the current instance. Missing or unauthorized revisions block export and require another choice, without a silent fallback. Read-only documents can still select styles and export. Extensions read the same resolved configuration through markdownExportTemplateService; explicit formatting and style references from custom conversions take precedence.

The existing MD Editor example includes the template library. Chinese and English examples persist their export selections separately. The private documentation host is commercially licensed; the basic editor and export protocol retain their existing licenses.

The first-party @quirekit/export-template-library and official design packs are separately commercially licensed. They provide browsing, creation, duplicate/edit, versioned saving and explicitly enabled local storage, installed by a commercial host through these public seams. Component and content-pack entitlements are separate; payment, accounts and production license verification belong to the host. Basic Markdown/Word export does not depend on commercial packages. Using uploaded DOCX/DOTX files as templates remain future capabilities.

In “Template management”, “New template” starts from the default configuration; “New from selected” copies the style currently being previewed. A primary border, soft background and check mark identify the selected card. Use the edit button at the top right of a card to modify that template. Built-in templates are read-only, so editing creates a copy with its provenance retained; editing a personal template saves a new revision of the same identity. “Currently used” marks the document's applied revision. Browsing, editing and saving alone do not apply a template; “Use this template” or “Save and use” updates the document's export settings.

Duplicating or editing a template keeps the window at a stable size. Categories replace only the scrollable fields; the template name, category tabs and save actions remain visible. Narrow screens use a compact preview, with limited-height screens prioritizing the editable fields. Fields are grouped by appearance, text and paragraph settings, with units beside numeric values. Click a color swatch to choose a preset or open “More colors” for precise adjustment. Text colors can return to “Automatic” and backgrounds to “No fill”. Bold, italic and alignment use formatting buttons.

Custom node conversions and round trips

MD Editor registers containers and all three text-diagram conversions by default, following containers / extras.mermaid / extras.textDiagram:

NodeWord contentReimport
Single-paragraph containerParagraph shading, four borders and border spacingRestore container source after validation
Container with a title, lists or nested blocksSingle-cell table shading, borders and cell margins; body remains editableRestore the complete container after validation
Highlight blockEmoji as the first line, borderless shading and optional text colorRestore the highlight source after validation
Mermaid, PlantUML or Graphviz/DOTPNG rendered offscreen from sourceRestore the original fence and source after validation

Rounded corners and disclosure interactions are approximated with diagnostics. The default container parser recognizes closed named fences at the root and inside container bodies; containers inside lists or blockquotes need a host parser extension. Failed image rendering retains code. Export does not depend on previews being mounted in the current viewport.

Source metadata lives in a standard Custom XML part linked to content controls. Changes to visible content, image bytes, footnotes/endnotes or style/numbering dependencies invalidate restoration: import keeps current Word content instead of overwriting edits with stale source. Missing metadata, duplicate identities, unknown versions or absent extensions also retain native content or images. QuireKit round trips are verified; preservation after saving through Word/WPS or other producers is not yet guaranteed.

Use <MdEditor :docx="{ converter, preserveMetadata: false }" /> for native content and images only. builtins: false disables default node conversions while retaining conversions installed with mdDocx({ extensions }). Base MSFE packages have no DOCX dependency.

Register these features explicitly in the standalone adapter. The host supplies image rendering; conversion does not implicitly fetch resources:

ts

Custom features implement MarkdownDocxExtension.addDocx() using the same node contributions as Classic. Word/PDF/OFD share parsing, preparation, node codecs and template services. DOCX package passes and Custom XML recovery apply only to Word. See Export custom nodes to Word, PDF and OFD for complete native-card and image recipes, composition, cancellation and failure handling.

Validate payload versions and fields and retain visible fallback. Metadata is limited to 64 KiB per record and 1 MiB / 1000 records per document. preserveMetadata controls builtins; host extensions own their policy.

Share the Prose classes

Live and locked modes use qk-prose qk-prose--compact; MdProse shares the same compact density. Code blocks use qk-code-block qk-prose-source. Task controls use the public Checkbox with qk-prose-task-marker to preserve body line height and share their inline width with [x] / [ ] source. Containers use qk-prose-container, qk-prose-container__header, qk-prose-container__body and data-tone. The adapter maps CodeMirror blocks to native elements or data-qk-prose roles that consume the public Prose CSS. Reading and editing share the same style definitions.

Prefer the host's QuireKit theme for fonts, colors and spacing. For a business-specific container, scope an explicit variant to .qk-prose-container[data-container="your-type"]. Avoid painting individual lines or replacing CodeMirror-owned DOM. Retain the same container identifier when using the variant in static reading.

Capture a Reading view snapshot

vue

MdProse consumes immutable state and produces disposable semantic DOM. It does not create a second editable JSON document or serialize Markdown back. Registered container names are retained as data-container for shared variants. Reading supports core blocks, inline extensions, links, images, formulas, Mermaid, PlantUML, Graphviz/DOT, definition lists, footnotes and Frontmatter. Formulas and diagrams use the same safe renderers; metadata retains source presentation. Disabled diagram languages stay an ordinary code block. Third-party extensions can use withMdReading() from @quirekit/md-editor/vue, sharing their original codec validation and the reader's cancellation, cleanup and fallback lifecycle. See Custom nodes. Unknown nodes retain source text and source HTML is preserved as text by default. components and explicit html policies share the public reader's contract. Reading contributions do not automatically provide Word/PDF/OFD export support.

Plain text and streamed AI responses

Pass the complete source received so far to content without creating an editor. Update the same string as chunks arrive; replacement, clearing and switching messages use this property too. Do not pass state at the same time.

vue

status accepts receiving, complete, stopped and error. It describes transport only. Stopping or failing preserves source; late definitions may still correct earlier references. Blocks are never prematurely frozen. Open fences show code immediately; Mermaid and other previews start only after closure. Repeated footnote references have distinct anchors; duplicate link definitions use the first.

options accepts MdEditorExtensionOptions, including extras, containers and extensions. Register reading contributions with withMdReading. Replacing options or identity releases the old session and resources. Configuration must be pure and independent of a mounted editor: initialization cannot use this.editor or install a source provider. Failures emit MdReadingError; source setup or parse failures use phase: 'parse' and retain escaped source text.

Preparing the editor…

The example appends 20 UTF-16 code units every 30ms, with link and footnote definitions arriving last. Stop, load everything, or restart. This executable example uses MdProse, which renders identical escaped source on the server and first client frame, then parses after mount. Pass an accepted state for semantic SSR, or use the public reading component. MdProse also shares its components and explicit html policies. Additional parsing yields cooperatively, without a hard real-time guarantee. Large documents and dense references still involve global work. Stable top-level blocks reuse their projection when syntax identity, positions and dependencies are unchanged; growing blocks are projected again.

Framework-free hosts can use MdProseStream: call update(content), advance parsing with advance(budget), and consume state. A false result requires another host-scheduled slice; the class creates no timers. Incremark AST renderers, executable HTML and chart configurations are not compatible input to this API.

Framework-neutral assembly

createMdEditor(options) returns the original MsfeEditor contract. Lifecycle, commands and saving methods are unchanged. createMdEditorExtensions(options) returns the product extension set for an existing MSFE factory. vueMdEditorExtensions(locale, options, edit?) installs Vue task, image, code-language and disclosure controls and requires the MSFE Vue component host. Its third argument can handle formula/diagram editing. MdEditor already assembles these controls and the shared dialogs. The framework-neutral factory does not mount Vue toolbars or formula panels; provide editing callbacks through extras.math / extras.mermaid / extras.textDiagram, or use view: 'preview' to edit active-node source directly.

Highlight blocks

A highlight block is a body block with an emoji icon on the left and a light background, useful for calling out one reminder or conclusion. Type /highlight (or /callout) in an empty paragraph, or create one from the toolbar Insert menu or the block menu's Insert above / Insert below. The body is ordinary content: keep typing, split paragraphs, add lists or nest other blocks.

  • Click the icon to open the emoji picker; choosing one only replaces emoji on the opening line.
  • Open the highlight's block menu and choose Color settings. Highlight color offers six backgrounds (light orange, red, green, blue, purple and gray); Text color offers Default or the same colors. Each change is one undoable source replacement.
  • In read-only mode the icon is display-only. In Live preview, placing the caret inside the highlight reveals its complete source.
markdown

Attributes live in {} on the opening line: emoji defaults to 📌; color names the background and defaults to orange; text names the text color and is omitted for the default. Only color names are stored; editing and reading map them to the theme's semantic colors, so dark themes follow automatically. Unknown keys or names fall back to the defaults without rewriting the source. Renderers without ::: container support show the fence lines as plain text and the body as ordinary Markdown, so nothing is lost.

Highlights use the default containers name highlight. Setting containers.types replaces the whole list, so include highlight when you still need it. MdProse, Word, PDF and OFD export use the same source: exports write the icon as the first line and fixed light approximations of the background and text color, and Word import restores the original source.

Dates, emoji and mentions

The style field uses the same bordered Select as Classic. Text formatting stays directly visible, and the grouped Insert menu gives each action an icon from the shared registry.

Insert → Date uses QuireCalendar. Browsing months does not edit the document. Choosing a day or Today inserts an ISO YYYY-MM-DD calendar date without time-zone conversion. Insert → Emoji uses QuireEmojiPicker. Common shortcuts and all categories share one continuously scrolling QuireScrollArea with the component library's custom scrollbar. Search and category navigation remain fixed. Category buttons scroll to their section; scrolling updates the active category. Bilingual search deduplicates results, and recent choices belong to the current editor session. Values are exact Unicode sequences, including composed emoji. Divider inserts an ordinary --- block. Each insertion uses the mapped source anchor captured when opening the menu and undoes once.

Pass mentions to enable a contact picker. The standalone example uses fictional local contacts. A static array is filtered by label, description or id; an asynchronous provider owns search and access filtering. The application implements the endpoint in this example:

vue

Choose Insert → Mention, or type @ after whitespace or an opening bracket in text. Search inside the picker, then use the arrow keys and Enter, or click a contact. The typed trigger replaces only its @; the menu action replaces the captured selection. Cancel preserves the typed @; search drafts never enter the document. Pasted text, email addresses, code, formulas and links do not trigger suggestions. Configure mentions at mount to install the trigger. Replacing an installed provider updates its results; enabling the feature after an initially unconfigured mount requires a remount.

The shared SuggestionController correlates requests, aborts obsolete queries and suppresses queries during composition. Failures offer Retry. A stale source anchor, document replacement or read-only state rejects insertion. Each item needs a stable id, a nonempty label and a safe url. The result is a portable [@Lin](</people/lin>) Markdown link. No hidden identity document or notification service is created; notifications, permissions and member profile pages belong to the application.

QuireEmojiPicker is independently exported from @quirekit/ui/vue. Replace its catalog with items and groups, configure shortcut order with common ([] hides it), supply host-owned recent values, and handle select for the item and its exact Unicode value. The component has no editor dependency, overlay or persistent storage. Its default catalog is curated rather than a complete Unicode catalog; a skin-tone selector is not included. Common shortcuts are curated defaults rather than frequency-based rankings; recent choices appear in their own section.

ts
MSFE · QuireKitYour Markdown source stays yours.