Schema Definition
Schema definition for the Code Block node type:
{
codeBlock: {
content: 'text*',
group: 'block',
attrs: {
language: { default: null }
},
}
}Model Representation
Example model representation:
{
type: 'codeBlock',
attrs: { language: 'javascript' },
children: [
{ type: 'text', text: 'const x = 1;\nconst y = 2;' }
]
}HTML Serialization
Converting model to HTML:
function serializeCodeBlock(node) {
const language = node.attrs?.language || '';
const code = node.children.map(child => child.text).join('');
const attrs = language ? { 'data-language': language } : {};
return '<pre' + serializeAttrs(attrs) + '><code>' +
escapeHtml(code) + '</code></pre>';
}HTML Deserialization
Parsing HTML to model:
function parseCodeBlock(domNode) {
const pre = domNode.tagName === 'PRE' ? domNode : domNode.closest('pre');
if (!pre) return null;
const code = pre.querySelector('code') || pre;
const language = pre.getAttribute('data-language') ||
code.getAttribute('data-language') || null;
return {
type: 'codeBlock',
attrs: { language },
children: [{
type: 'text',
text: code.textContent
}]
};
}View Integration
View Integration Notes: Pay special attention to contenteditable behavior, selection handling, and event management when implementing this node type in your view layer.
View integration code:
// Rendering code block
const pre = document.createElement('pre');
const code = document.createElement('code');
pre.contentEditable = 'false'; // Code blocks are typically read-only
code.textContent = node.children[0].text;
if (node.attrs?.language) {
code.setAttribute('data-language', node.attrs.language);
}
pre.appendChild(code);
// Handling Enter key in code block
function handleEnterInCodeBlock(e) {
e.preventDefault();
insertText('\n');
}Common Issues
Common Pitfalls: These are issues frequently encountered when implementing this node type. Review carefully before implementation.
Common issues and solutions:
// Issue: Whitespace preservation
// Solution: Use pre tag and preserve whitespace
function preserveWhitespace(text) {
return text.replace(/ /g, ' ').replace(/\n/g, '<br>');
}
// Issue: Syntax highlighting
// Solution: Apply highlighting after rendering
function applySyntaxHighlighting(codeElement, language) {
// Use a syntax highlighter library
// Highlight after DOM insertion
}
// Issue: Tab key handling
// Solution: Insert spaces instead of tab
function handleTabInCodeBlock(e) {
e.preventDefault();
insertText(' '); // Insert 2 spaces
}Implementation
Complete implementation example:
class CodeBlockNode {
constructor(attrs, children) {
this.type = 'codeBlock';
this.attrs = { language: attrs?.language || null };
this.children = children || [];
}
toDOM() {
const pre = document.createElement('pre');
const code = document.createElement('code');
code.textContent = this.children[0]?.text || '';
if (this.attrs.language) {
code.setAttribute('data-language', this.attrs.language);
}
pre.appendChild(code);
return pre;
}
static fromDOM(domNode) {
const pre = domNode.tagName === 'PRE' ? domNode : domNode.closest('pre');
if (!pre) return null;
const code = pre.querySelector('code') || pre;
return new CodeBlockNode(
{ language: pre.getAttribute('data-language') },
[{ type: 'text', text: code.textContent }]
);
}
}