Skip to content

Commit

Permalink
Make images block elements
Browse files Browse the repository at this point in the history
Use markdown-it-image-figures to render standalone images inside
`<figure>` elements instead of `<p>` when markdown source gets parsed
into HTML. This allows to treat images as block elements by TipTap.

Also keep support for inline images to not break markdown files that
have inline images.

Fixes: #2873

Signed-off-by: Jonas <jonas@freesources.org>
  • Loading branch information
mejo- committed Oct 31, 2022
1 parent 732c868 commit c9b01f3
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 3 deletions.
6 changes: 3 additions & 3 deletions src/extensions/RichText.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -82,9 +83,8 @@ export default Extension.create({
TaskItem,
Callout,
Underline,
Image.configure({
inline: true,
}),
Image,
ImageInline,
Dropcursor,
KeepSyntax,
FrontMatter,
Expand Down
2 changes: 2 additions & 0 deletions src/markdownit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -15,5 +16,6 @@ const markdownit = MarkdownIt('commonmark', { html: false, breaks: false })
.use(callouts)
.use(keepSyntax)
.use(markdownitMentions)
.use(implicitFigures)

export default markdownit
10 changes: 10 additions & 0 deletions src/nodes/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 74 additions & 0 deletions src/nodes/ImageInline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* @copyright Copyright (c) 2022 Jonas <jonas@freesources.org>
*
* @author Jonas <jonas@freesources.org>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

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

0 comments on commit c9b01f3

Please sign in to comment.