import { $prose } from '@milkdown/kit/utils' import { Plugin, TextSelection } from '@milkdown/kit/prose/state' import { Decoration, DecorationSet } from '@milkdown/kit/prose/view' import type { Editor } from '@milkdown/kit/core' import { editorViewCtx } from '@milkdown/kit/core' const openingTag = /^$/i const closingTag = /^<\/span>$/i export const fontSizeMarkdownPlugin = $prose(() => new Plugin({ props: { decorations(state) { const decorations: Decoration[] = [] const stack: Array<{ from: number; size: string }> = [] state.doc.descendants((node, position) => { if (node.type.name !== 'html') return const value = String(node.attrs.value ?? '') const match = value.match(openingTag) if (match) { stack.push({ from: position + node.nodeSize, size: match[1] }) decorations.push(Decoration.node(position, position + node.nodeSize, { class: 'font-size-marker' })) return } if (closingTag.test(value)) { const opening = stack.pop() decorations.push(Decoration.node(position, position + node.nodeSize, { class: 'font-size-marker' })) if (opening && opening.from < position) { decorations.push(Decoration.inline(opening.from, position, { style: `font-size: ${opening.size}px`, 'data-font-size': opening.size, })) } } }) return DecorationSet.create(state.doc, decorations) }, }, })) export function applyMarkdownFontSize(editor: Editor, size: number): boolean { return editor.action((ctx) => { const view = ctx.get(editorViewCtx) const { from, to, empty } = view.state.selection if (empty) return false const htmlNode = view.state.schema.nodes.html if (!htmlNode) return false const opening = htmlNode.create({ value: `` }) const closing = htmlNode.create({ value: '' }) const transaction = view.state.tr .insert(to, closing) .insert(from, opening) transaction.setSelection(TextSelection.create(transaction.doc, from + 1, to + 1)) view.dispatch(transaction) view.focus() return true }) }