From 9b8e4d1d7030f573f3a364d3c60f9541f5328f00 Mon Sep 17 00:00:00 2001 From: suyao Date: Wed, 29 Oct 2025 08:04:21 +0800 Subject: [PATCH] Add enhanced horizontal rule extension with markdown indentation support - Create EnhancedHorizontalRule extension that preserves leading spaces (0-3) in markdown serialization - Store indentation as data attribute and restore it during markdown rendering - Fix YAML front matter parsing to not match indented horizontal rules as front matter --- .../extensions/enhanced-horizontal-rule.ts | 74 +++++++++++++++++++ .../extensions/yaml-front-matter.ts | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/renderer/src/components/RichEditor/extensions/enhanced-horizontal-rule.ts diff --git a/src/renderer/src/components/RichEditor/extensions/enhanced-horizontal-rule.ts b/src/renderer/src/components/RichEditor/extensions/enhanced-horizontal-rule.ts new file mode 100644 index 0000000000..8dac0f901f --- /dev/null +++ b/src/renderer/src/components/RichEditor/extensions/enhanced-horizontal-rule.ts @@ -0,0 +1,74 @@ +import { mergeAttributes } from '@tiptap/core' +import { HorizontalRule } from '@tiptap/extension-horizontal-rule' + +/** + * Enhanced HorizontalRule extension that preserves leading spaces in markdown + * + * Standard Markdown allows 0-3 spaces before a horizontal rule (---, ***, ___) + * This extension preserves that indentation when serializing back to markdown + */ +export const EnhancedHorizontalRule = HorizontalRule.extend({ + addAttributes() { + return { + ...this.parent?.(), + // Store the number of leading spaces (0-3) + indentation: { + default: 0, + parseHTML: (element) => { + const indent = element.getAttribute('data-indentation') + return indent ? parseInt(indent, 10) : 0 + }, + renderHTML: (attributes) => { + if (!attributes.indentation) return {} + return { + 'data-indentation': attributes.indentation + } + } + } + } + }, + + parseHTML() { + return [ + { + tag: 'hr', + getAttrs: (element) => { + if (typeof element === 'string') return {} + + const htmlElement = element as HTMLElement + const indentAttr = htmlElement.getAttribute('data-indentation') + + return { + indentation: indentAttr ? parseInt(indentAttr, 10) : 0 + } + } + } + ] + }, + + renderHTML({ HTMLAttributes }) { + return ['hr', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)] + }, + + // Custom markdown parsing to capture leading spaces + parseMarkdown(token) { + // The token.raw contains the original markdown including leading spaces + // Match 0-3 leading spaces before ---, ***, or ___ + const match = /^( {0,3})(?:---|___|\*\*\*)/.exec(token.raw || '') + const indentation = match ? match[1].length : 0 + + return { + type: this.name, + attrs: { + indentation + } + } + }, + + // Custom markdown serialization to restore leading spaces + renderMarkdown(node) { + const indentation = node.attrs?.indentation || 0 + const spaces = ' '.repeat(Math.min(indentation, 3)) // Max 3 spaces per spec + return spaces + '---\n\n' + } +}) diff --git a/src/renderer/src/components/RichEditor/extensions/yaml-front-matter.ts b/src/renderer/src/components/RichEditor/extensions/yaml-front-matter.ts index f0162517a4..67c51c0553 100644 --- a/src/renderer/src/components/RichEditor/extensions/yaml-front-matter.ts +++ b/src/renderer/src/components/RichEditor/extensions/yaml-front-matter.ts @@ -23,7 +23,7 @@ export const YamlFrontMatter = Node.create({ level: 'block', start(src: string) { - const result = src.match(/^\s*---\n/) ? 0 : -1 + const result = src.match(/^---\n/) ? 0 : -1 return result }, // Parse YAML front matter