From f9e1445b9a11120819be6584e3e9b699d16262f8 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sun, 24 Jul 2022 13:23:28 +0200 Subject: [PATCH 1/3] Better styling of Front Matter + input rule Added an input rule to add a front matter node if `---` is typed in the very beginning of the document. If it is typed somewhere else the default behavior is still to insert a `hr` node. Added a border on the bottom of the front matter and added a title for the element. Signed-off-by: Ferdinand Thiessen --- css/prosemirror.scss | 12 +++++++++++ src/nodes/FrontMatter.js | 43 ++++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/css/prosemirror.scss b/css/prosemirror.scss index b0e14da14eb..c858bf3dbf3 100644 --- a/css/prosemirror.scss +++ b/css/prosemirror.scss @@ -163,6 +163,18 @@ div.ProseMirror { margin-bottom: 1em; } + pre.frontmatter { + margin-bottom: 2em; + border-bottom: 2px solid var(--color-primary-element); + } + + pre.frontmatter::before { + display: block; + content: attr(data-title); + color: var(--color-text-maxcontrast); + padding-bottom: 0.5em; + } + p code { background-color: var(--color-background-dark); border-radius: var(--border-radius); diff --git a/src/nodes/FrontMatter.js b/src/nodes/FrontMatter.js index d374228d1b6..1083a741893 100644 --- a/src/nodes/FrontMatter.js +++ b/src/nodes/FrontMatter.js @@ -1,15 +1,17 @@ +import { mergeAttributes } from '@tiptap/core' import TiptapCodeBlock from '@tiptap/extension-code-block' const FrontMatter = TiptapCodeBlock.extend({ name: 'frontMatter', - addAttributes() { - return { - ...this.parent?.(), - class: { - default: 'frontmatter', - rendered: true, - }, - } + // FrontMatter are only valid at the begin of a document + draggable: false, + + renderHTML({ node, HTMLAttributes }) { + return this.parent({ + node, + HTMLAttributes: + mergeAttributes(HTMLAttributes, { 'data-title': t('text', 'Front matter'), class: 'frontmatter' }), + }) }, parseHTML: () => { return [ @@ -33,13 +35,32 @@ const FrontMatter = TiptapCodeBlock.extend({ state.write('---') state.closeBlock(node) }, - // FrontMatter are only valid at the begin of a document - draggable: false, + + // Allow users to add a FrontMatter, but only at the beginning of the document + addInputRules() { + return [ + { + find: /^---$/g, + handler: ({ state, range, chain }) => { + if (range.from === 1) { + if (state.doc.resolve(1).parent.type.name === this.name) return false + chain() + .deleteRange(range) + .insertContentAt(0, { + type: this.name, + }) + return true + } + return false + }, + }, + ] + }, + // Override rules from Codeblock addCommands() { return {} }, - addInputRules: () => [], addPasteRules: () => [], addProseMirrorPlugins: () => [], }) From 07cb6bff8ac409b62e73b86c82fdeeb063df540a Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 26 Jul 2022 09:59:05 +0200 Subject: [PATCH 2/3] Added tests for the front matter node Signed-off-by: Ferdinand Thiessen --- cypress/e2e/FrontMatter.spec.js | 62 +++++++++++++++++++++++++++++++++ cypress/fixtures/frontmatter.md | 6 ++++ src/tests/markdown.spec.js | 6 ++++ 3 files changed, 74 insertions(+) create mode 100644 cypress/e2e/FrontMatter.spec.js create mode 100644 cypress/fixtures/frontmatter.md diff --git a/cypress/e2e/FrontMatter.spec.js b/cypress/e2e/FrontMatter.spec.js new file mode 100644 index 00000000000..1a7200779a0 --- /dev/null +++ b/cypress/e2e/FrontMatter.spec.js @@ -0,0 +1,62 @@ +/** + * @copyright Copyright (c) 2022 + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import { initUserAndFiles, randHash } from '../utils/index.js' + +const randUser = randHash() + +describe('Front matter support', function() { + before(function() { + initUserAndFiles(randUser, 'frontmatter.md', 'empty.md') + }) + + beforeEach(function() { + cy.login(randUser, 'password') + }) + + it('Open file with front matter', function() { + cy.openFile('frontmatter.md').then(() => { + expect(cy.getContent().find('pre.frontmatter').length === 1) + }) + }) + + it('Add front matter', function() { + cy.openFile('empty.md').clearContent().then(() => { + cy.getContent() + .type('---') + .type('test') + cy.getContent().find('pre.frontmatter').should(pre => { + expect(pre.length === 1) + expect(pre[0].text === 'test') + }) + }) + }) + + it('Do not add multiple front matter', function() { + cy.openFile('empty.md').clearContent().then(() => { + cy.getContent() + .type('---test') + .type('{downArrow}') + .type('---test') + cy.getContent().find('pre.frontmatter').should(pre => expect(pre.length === 1)) + cy.getContent().find('hr').should(hr => expect(hr.length === 1)) + }) + }) +}) diff --git a/cypress/fixtures/frontmatter.md b/cypress/fixtures/frontmatter.md new file mode 100644 index 00000000000..5698c55c5b8 --- /dev/null +++ b/cypress/fixtures/frontmatter.md @@ -0,0 +1,6 @@ +--- +some: value +other: 1.2 +--- +# Heading +Content \ No newline at end of file diff --git a/src/tests/markdown.spec.js b/src/tests/markdown.spec.js index ae7ed3cfec2..ca4b264363a 100644 --- a/src/tests/markdown.spec.js +++ b/src/tests/markdown.spec.js @@ -192,4 +192,10 @@ describe('Markdown serializer from html', () => { `

!warning!

` )).toBe(`::: warn\n!warning!\n\n:::`) }) + + test('front matter', () => { + expect(markdownThroughEditorHtml('
some: value

Heading

')).toBe('---\nsome: value\n---\n\n# Heading') + // Test --- within front matter is allowed + expect(markdownThroughEditorHtml('
---

Heading

')).toBe('----\n---\n----\n\n# Heading') + }) }) From 9e5082e70f335cab8d3a2471e5b54f73eac9237d Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 26 Jul 2022 10:54:34 +0200 Subject: [PATCH 3/3] Front matter: Handle dashes in content If the front matter contains a sequence of three or more dashes, the front matter fences must be adjusted. Signed-off-by: Ferdinand Thiessen --- src/nodes/FrontMatter.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/nodes/FrontMatter.js b/src/nodes/FrontMatter.js index 1083a741893..04f0b06f591 100644 --- a/src/nodes/FrontMatter.js +++ b/src/nodes/FrontMatter.js @@ -13,26 +13,29 @@ const FrontMatter = TiptapCodeBlock.extend({ mergeAttributes(HTMLAttributes, { 'data-title': t('text', 'Front matter'), class: 'frontmatter' }), }) }, - parseHTML: () => { - return [ - { - tag: 'pre[id="frontmatter"]', - preserveWhitespace: 'full', - priority: 9001, - attrs: { - language: 'yaml', - }, + parseHTML() { + return [{ + tag: 'pre#frontmatter', + preserveWhitespace: 'full', + priority: 9001, + attrs: { + language: 'yaml', }, - ] + }] }, toMarkdown: (state, node) => { if (!state.out.match(/^\s*/)) throw Error('FrontMatter must be the first node of the document!') + const text = node.textContent + // Make sure the front matter fences are longer than any dash sequence within it + const dashes = text.match(/-{3,}/gm) + const separator = '-'.repeat(dashes ? dashes.sort().slice(-1)[0].length + 1 : 3) + state.write('') state.out = '' - state.write('---\n') - state.text(node.textContent, false) + state.write(`${separator}\n`) + state.text(text, false) state.ensureNewLine() - state.write('---') + state.write(separator) state.closeBlock(node) },