diff --git a/src/extensions/RichText.js b/src/extensions/RichText.js index d5807c3e737..0590c4b11b8 100644 --- a/src/extensions/RichText.js +++ b/src/extensions/RichText.js @@ -8,7 +8,6 @@ import { Extension } from '@tiptap/core' /* eslint-disable import/no-named-as-default */ import Blockquote from '@tiptap/extension-blockquote' import Document from '@tiptap/extension-document' -import HorizontalRule from '@tiptap/extension-horizontal-rule' import { ListItem } from '@tiptap/extension-list' import Placeholder from '@tiptap/extension-placeholder' import Text from '@tiptap/extension-text' @@ -17,6 +16,7 @@ import { CharacterCount, Dropcursor, Gapcursor } from '@tiptap/extensions' import { common, createLowlight } from 'lowlight' import MentionSuggestion from '../components/Suggestion/Mention/suggestions.js' import Heading from '../nodes/Heading.js' +import HorizontalRule from '../nodes/HorizontalRule.ts' import EmojiSuggestion from './../components/Suggestion/Emoji/suggestions.js' import LinkBubble from './../extensions/LinkBubble.js' import LinkPicker from './../extensions/LinkPicker.js' diff --git a/src/markdownit/index.js b/src/markdownit/index.js index d2ca8c7c809..2a4f8898d0a 100644 --- a/src/markdownit/index.js +++ b/src/markdownit/index.js @@ -48,6 +48,10 @@ const markdownit = MarkdownIt('commonmark', { html: false, breaks: false }) markdownit.renderer.rules.front_matter = (tokens, idx, options) => `
${escapeHtml(tokens[idx].meta)}
` +// Render horizontal rules with markup attribute +markdownit.renderer.rules.hr = (tokens, idx) => + `
\n` + // Render lists with bullet attribute markdownit.renderer.rules.bullet_list_open = (tokens, idx, options) => { tokens[idx].attrs = [ diff --git a/src/nodes/HorizontalRule.ts b/src/nodes/HorizontalRule.ts new file mode 100644 index 00000000000..1483d5c1fab --- /dev/null +++ b/src/nodes/HorizontalRule.ts @@ -0,0 +1,39 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { nodeInputRule } from '@tiptap/core' +import TiptapHorizontalRule from '@tiptap/extension-horizontal-rule' + +const HorizontalRule = TiptapHorizontalRule.extend({ + addAttributes() { + return { + markup: { + default: '---', + parseHTML: (el) => el.getAttribute('data-markup') || '---', + renderHTML: (attrs) => { + if (!attrs.markup || attrs.markup === '---') { + return {} + } + return { 'data-markup': attrs.markup } + }, + }, + } + }, + + // Same triggers as upsream, but capture typed marker so it can be preserved + addInputRules() { + return [ + nodeInputRule({ + find: /^(?:(---|—-)|(___|\*\*\*)\s)$/, + type: this.type, + getAttributes: (match) => ({ + markup: match[2] ?? '---', + }), + }), + ] + }, +}) + +export default HorizontalRule diff --git a/src/tests/markdown.spec.js b/src/tests/markdown.spec.js index c117ffbbf0b..357464f9932 100644 --- a/src/tests/markdown.spec.js +++ b/src/tests/markdown.spec.js @@ -135,6 +135,12 @@ describe('Markdown though editor', () => { expect(markdownThroughEditor('foo\n\n---\n\nfoobar')).toBe( 'foo\n\n---\n\nfoobar', ) + expect(markdownThroughEditor('***\n\n- [ ] task\n\n***')).toBe( + '***\n\n- [ ] task\n\n***', + ) + expect(markdownThroughEditor('___\n\n- [ ] task\n\n___')).toBe( + '___\n\n- [ ] task\n\n___', + ) }) test('table', () => { diff --git a/src/tests/markdownit/commonmark.spec.js b/src/tests/markdownit/commonmark.spec.js index c32d1278a89..c25bd7f6a96 100644 --- a/src/tests/markdownit/commonmark.spec.js +++ b/src/tests/markdownit/commonmark.spec.js @@ -24,6 +24,7 @@ describe('Commonmark', () => { .replace(/
<\/blockquote>/g, '
\n
') .replace(/([^<]+)<\/span>/g, '$1') .replace(/
/g, '
\n') + .replace(/