feat: enable Prism-based syntax highlighting in code blocks

- Register CodeHighlightNode in the editor node set
- Add CodeHighlightPlugin that calls registerCodeHighlighting
- Map codeHighlight theme keys to mc-tok-* CSS classes
- Add dark-theme token colors for 20+ Prism token types

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-25 19:52:41 +01:00
co-authored by Copilot
parent 38d7e339a2
commit 8240468d1e
3 changed files with 68 additions and 2 deletions
+43 -1
View File
@@ -20,7 +20,7 @@ import {
$convertFromMarkdownString,
$convertToMarkdownString,
} from '@lexical/markdown';
import { $isCodeNode, $createCodeNode, CodeNode } from '@lexical/code';
import { $isCodeNode, $createCodeNode, CodeNode, registerCodeHighlighting } from '@lexical/code';
import {
$isListNode,
INSERT_ORDERED_LIST_COMMAND,
@@ -73,6 +73,38 @@ const editorTheme: EditorThemeClasses = {
},
quote: 'mc-blockquote',
code: 'mc-code-block',
codeHighlight: {
atrule: 'mc-tok-atrule',
attr: 'mc-tok-attr',
boolean: 'mc-tok-boolean',
builtin: 'mc-tok-builtin',
cdata: 'mc-tok-comment',
char: 'mc-tok-string',
class: 'mc-tok-function',
'class-name': 'mc-tok-function',
comment: 'mc-tok-comment',
constant: 'mc-tok-constant',
deleted: 'mc-tok-deleted',
doctype: 'mc-tok-comment',
entity: 'mc-tok-symbol',
function: 'mc-tok-function',
important: 'mc-tok-keyword',
inserted: 'mc-tok-inserted',
keyword: 'mc-tok-keyword',
namespace: 'mc-tok-namespace',
number: 'mc-tok-number',
operator: 'mc-tok-operator',
prolog: 'mc-tok-comment',
property: 'mc-tok-property',
punctuation: 'mc-tok-punctuation',
regex: 'mc-tok-regex',
selector: 'mc-tok-selector',
string: 'mc-tok-string',
symbol: 'mc-tok-symbol',
tag: 'mc-tok-tag',
url: 'mc-tok-string',
variable: 'mc-tok-variable',
},
link: 'mc-link',
};
@@ -146,6 +178,15 @@ function EditorRefPlugin({ editorRef }: { editorRef: React.MutableRefObject<Lexi
return null;
}
/** Enables Prism-based syntax highlighting inside CodeNodes. */
function CodeHighlightPlugin() {
const [editor] = useLexicalComposerContext();
useEffect(() => {
return registerCodeHighlighting(editor);
}, [editor]);
return null;
}
/** Syncs the React `disabled` prop to the Lexical editable state. */
function EditablePlugin({ disabled }: { disabled: boolean }) {
const [editor] = useLexicalComposerContext();
@@ -556,6 +597,7 @@ export const MarkdownComposer = forwardRef<MarkdownComposerHandle, MarkdownCompo
<HistoryPlugin />
<ListPlugin />
<LinkPlugin />
<CodeHighlightPlugin />
<MarkdownShortcutPlugin transformers={[...markdownEditorTransformers]} />
<ClearEditorPlugin />
<ContentTrackingPlugin onContentChange={onContentChange} />
+2 -1
View File
@@ -1,4 +1,4 @@
import { CodeNode } from '@lexical/code';
import { CodeHighlightNode, CodeNode } from '@lexical/code';
import { AutoLinkNode, LinkNode } from '@lexical/link';
import { type Transformer, TRANSFORMERS } from '@lexical/markdown';
import { ListItemNode, ListNode } from '@lexical/list';
@@ -38,6 +38,7 @@ export const markdownEditorNodes: ReadonlyArray<Klass<LexicalNode>> = [
LinkNode,
AutoLinkNode,
CodeNode,
CodeHighlightNode,
];
export const markdownEditorTransformers: ReadonlyArray<Transformer> = TRANSFORMERS;
+23
View File
@@ -292,6 +292,29 @@ textarea {
text-underline-offset: 2px;
}
/* Syntax highlighting tokens (dark theme) */
.mc-tok-comment { color: #6b7280; font-style: italic; }
.mc-tok-keyword { color: #c084fc; }
.mc-tok-string { color: #86efac; }
.mc-tok-number { color: #fbbf24; }
.mc-tok-boolean { color: #fbbf24; }
.mc-tok-constant { color: #fbbf24; }
.mc-tok-function { color: #93c5fd; }
.mc-tok-builtin { color: #67e8f9; }
.mc-tok-operator { color: #f9a8d4; }
.mc-tok-punctuation { color: #a1a1aa; }
.mc-tok-property { color: #7dd3fc; }
.mc-tok-variable { color: #fca5a5; }
.mc-tok-tag { color: #f87171; }
.mc-tok-attr { color: #fdba74; }
.mc-tok-atrule { color: #c084fc; }
.mc-tok-selector { color: #86efac; }
.mc-tok-regex { color: #fcd34d; }
.mc-tok-symbol { color: #fbbf24; }
.mc-tok-namespace { color: #f87171; opacity: 0.8; }
.mc-tok-deleted { color: #fca5a5; text-decoration: line-through; }
.mc-tok-inserted { color: #86efac; }
/* React Flow dark theme overrides */
.react-flow__node {
font-family: inherit;