diff --git a/src/extensions/RichText.js b/src/extensions/RichText.js index 82b34d9a653..82361a294f4 100644 --- a/src/extensions/RichText.js +++ b/src/extensions/RichText.js @@ -37,6 +37,7 @@ import HardBreak from './HardBreak.js' import Heading from '../nodes/Heading/index.js' import HorizontalRule from '@tiptap/extension-horizontal-rule' import Image from './../nodes/Image.js' +import ImageInline from './../nodes/ImageInline.js' import KeepSyntax from './KeepSyntax.js' import ListItem from '@tiptap/extension-list-item' import Mention from './../extensions/Mention.js' @@ -82,9 +83,8 @@ export default Extension.create({ TaskItem, Callout, Underline, - Image.configure({ - inline: true, - }), + Image, + ImageInline, Dropcursor, KeepSyntax, FrontMatter, diff --git a/src/markdownit/index.js b/src/markdownit/index.js index 71c36cc9280..545f076fe4e 100644 --- a/src/markdownit/index.js +++ b/src/markdownit/index.js @@ -5,6 +5,7 @@ import underline from './underline.js' import splitMixedLists from './splitMixedLists.js' import callouts from './callouts.js' import keepSyntax from './keepSyntax.js' +import implicitFigures from 'markdown-it-image-figures' const markdownit = MarkdownIt('commonmark', { html: false, breaks: false }) .enable('strikethrough') @@ -15,5 +16,6 @@ const markdownit = MarkdownIt('commonmark', { html: false, breaks: false }) .use(callouts) .use(keepSyntax) .use(markdownitMentions) + .use(implicitFigures) export default markdownit diff --git a/src/nodes/Image.js b/src/nodes/Image.js index 3660c04e72e..137bd7715f3 100644 --- a/src/nodes/Image.js +++ b/src/nodes/Image.js @@ -29,6 +29,16 @@ const Image = TiptapImage.extend({ selectable: false, + parseHTML() { + return [ + { + tag: this.options.allowBase64 + ? 'figure img[src]' + : 'figure img[src]:not([src^="data:"])', + }, + ] + }, + renderHTML() { // Avoid the prosemirror node creation to trigger image loading as we use a custom node view anyways // Otherwise it would attempt to load the image from the current location before the node view is even initialized diff --git a/src/nodes/ImageInline.js b/src/nodes/ImageInline.js new file mode 100644 index 00000000000..fc82eb71385 --- /dev/null +++ b/src/nodes/ImageInline.js @@ -0,0 +1,74 @@ +/* + * @copyright Copyright (c) 2022 Jonas + * + * @author Jonas + * + * @license GNU AGPL version 3 or any later version + * + * 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 TiptapImage from '@tiptap/extension-image' +import ImageView from './ImageView.vue' +import { VueNodeViewRenderer } from '@tiptap/vue-2' + +// Inline image extension. Needed if markdown contains inline images. +// Not supported to be created from our UI (we default to block images). +const ImageInline = TiptapImage.extend({ + name: 'image-inline', + + // Lower priority than (block) Image extension + priority: 99, + + selectable: false, + + parseHTML() { + return [ + { + tag: this.options.allowBase64 + ? 'img[src]' + : 'img[src]:not([src^="data:"])', + }, + ] + }, + + addOptions() { + return { + ...this.parent?.(), + inline: true, + } + }, + + // Empty commands, we want only those from (block) Image extension + addCommands() { + return {} + }, + + // Empty input rules, we want only those from (block) Image extension + addInputRules() { + return [] + }, + + addNodeView() { + return VueNodeViewRenderer(ImageView) + }, + + toMarkdown(state, node) { + state.write('![' + state.esc(node.attrs.alt || '') + '](' + node.attrs.src.replace(/[()]/g, '\\$&') + + (node.attrs.title ? ' "' + node.attrs.title.replace(/"/g, '\\"') + '"' : '') + ')') + }, +}) + +export default ImageInline