Skip to content

Commit

Permalink
Merge pull request #3282 from nextcloud/fix/image_block
Browse files Browse the repository at this point in the history
Make images block nodes
  • Loading branch information
max-nextcloud authored Nov 15, 2022
2 parents 849d3d2 + 4eb1c53 commit 4403f08
Show file tree
Hide file tree
Showing 24 changed files with 235 additions and 40 deletions.
4 changes: 2 additions & 2 deletions js/editor.js

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions js/editor.js.LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,28 @@
*
*/

/*
* @copyright Copyright (c) 2022 Jonas <[email protected]>
*
* @author Jonas <[email protected]>
*
* @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/>.
*
*/

/*
* @copyright Copyright (c) 2022 Max <[email protected]>
*
Expand Down
2 changes: 1 addition & 1 deletion js/editor.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/files-modal.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion js/files-modal.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions js/text-files.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-files.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-public.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-public.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-text.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-text.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-viewer.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-viewer.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/vendors.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/vendors.js.map

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0",
"markdown-it-front-matter": "^0.2.3",
"markdown-it-image-figures": "^2.1.0",
"mitt": "^3.0.0",
"path-normalize": "^6.0.7",
"prosemirror-collab": "^1.3.0",
Expand Down
20 changes: 17 additions & 3 deletions src/components/Editor/MediaHandler.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
-->

<template>
<div class="editor editor-midia-handler"
data-text-el="editor-midia-handler"
<div class="editor editor-media-handler"
data-text-el="editor-media-handler"
:class="{ draggedOver }"
@image-paste="onPaste"
@dragover.prevent.stop="setDraggedOver(true)"
Expand Down Expand Up @@ -194,7 +194,21 @@ export default {
? this.$editor.chain().focus(position)
: this.$editor.chain()
chain.setImage({ src, alt }).insertContent('<br />').focus().run()
chain.setImage({ src, alt }).run()
const selection = this.$editor.view.state.selection
if (!selection.empty) {
// If inserted image is first element, it is selected and would get overwritten by
// subsequent editor inserts (see tiptap#3355). So unselect the image by placing
// the cursor at the end of the selection.
this.$editor.commands.focus(selection.to)
} else {
// Place the cursor after the inserted image node
this.$editor.commands.focus(selection.to + 2)
}
// Insert a newline to allow placing the cursor in between subsequent images
this.$editor.chain().insertContent('<br />').focus().run()
},
},
}
Expand Down
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,6 +16,7 @@ const markdownit = MarkdownIt('commonmark', { html: false, breaks: false })
.use(callouts)
.use(keepSyntax)
.use(markdownitMentions)
.use(implicitFigures)

// Issue #3370: To preserve softbreaks within md files we preserve all whitespaces, so we must not introduce additional new lines after a <br> element
markdownit.renderer.rules.hardbreak = (tokens, idx, options) => (options.xhtmlOut ? '<br />' : '<br>')
Expand Down
16 changes: 16 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 Expand Up @@ -83,6 +93,12 @@ const Image = TiptapImage.extend({
]
},

// Append two newlines after image to make it a block image
toMarkdown(state, node) {
state.write('![' + state.esc(node.attrs.alt || '') + '](' + node.attrs.src.replace(/[()]/g, '\\$&')
+ (node.attrs.title ? ' "' + node.attrs.title.replace(/"/g, '\\"') + '"' : '') + ')\n\n')
},

})

export default Image
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 <[email protected]>
*
* @author Jonas <[email protected]>
*
* @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
Loading

0 comments on commit 4403f08

Please sign in to comment.