Overview
Many modern editors (Notion, Slack, Discord)
support "Markdown Shortcuts"—typing #
creates a heading, **text** makes
it bold. Full Markdown import/export is also a common
requirement.
Markdown to Model
To import Markdown, you need a parser like marked or remark, then transform the AST into your model.
import { marked } from 'marked';
function importMarkdown(md) {
const tokens = marked.lexer(md);
const doc = { type: 'doc', children: [] };
tokens.forEach(token => {
if (token.type === 'heading') {
doc.children.push({
type: 'heading',
attrs: { level: token.depth },
content: token.text
});
}
// ... handle other tokens
});
return doc;
}Model to Markdown
To export key-value models to Markdown, implement a serializer that recurses through your node tree.
function serialize(node) {
if (node.type === 'text') return node.text;
if (node.type === 'paragraph') {
return node.children.map(serialize).join('') + '\n\n';
}
if (node.type === 'heading') {
return '#'.repeat(node.attrs.level) + ' ' +
node.children.map(serialize).join('') + '\n\n';
}
// ... handle other types
}Markdown Shortcuts
For auto-formatting (e.g., typing * lists),
listen to input or beforeinput events and check the text preceding the cursor.
- Check for trigger characters (space, enter).
-
Regex match the line content (e.g.,
/^#\s/). - Delete the markdown characters and change the block type.
Export Options
Beyond Markdown, considering supporting:
- HTML: Standard serialization (see HTML Mapping).
- Plain Text: Strip all tags and block structure.
- PDF: Use
window.print()CSS media queries or server-side generation (Puppeteer).