feat: convert pasted markdown to html so that it is correctly rendered (#3041)

Resolves https://community.vikunja.io/t/markdown-as-first-class-citizen/2975/4

Reviewed-on: https://kolaente.dev/vikunja/vikunja/pulls/3041
Co-authored-by: kolaente <k@knt.li>
Co-committed-by: kolaente <k@knt.li>
This commit is contained in:
kolaente 2025-02-21 15:53:47 +00:00 committed by konrad
parent 021d71b90e
commit f52a321acf
3 changed files with 41 additions and 0 deletions

View File

@ -93,6 +93,7 @@
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lowlight": "3.3.0",
"marked": "^15.0.6",
"pinia": "2.3.1",
"register-service-worker": "1.7.2",
"sortablejs": "1.15.6",

View File

@ -148,6 +148,9 @@ importers:
lowlight:
specifier: 3.3.0
version: 3.3.0
marked:
specifier: ^15.0.6
version: 15.0.6
pinia:
specifier: 2.3.1
version: 2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
@ -4754,6 +4757,11 @@ packages:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
marked@15.0.6:
resolution: {integrity: sha512-Y07CUOE+HQXbVDCGl3LXggqJDbXDP2pArc2C1N1RRMN0ONiShoSsIInMd5Gsxupe7fKLpgimTV+HOJ9r7bA+pg==}
engines: {node: '>= 18'}
hasBin: true
mdn-data@2.0.28:
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
@ -11719,6 +11727,8 @@ snapshots:
punycode.js: 2.3.1
uc.micro: 2.1.0
marked@15.0.6: {}
mdn-data@2.0.28: {}
mdn-data@2.0.30: {}

View File

@ -146,6 +146,8 @@ import EditorToolbar from './EditorToolbar.vue'
import StarterKit from '@tiptap/starter-kit'
import {Extension, mergeAttributes} from '@tiptap/core'
import {BubbleMenu, EditorContent, type Extensions, useEditor} from '@tiptap/vue-3'
import {Plugin, PluginKey} from '@tiptap/pm/state'
import {marked} from 'marked'
import Link from '@tiptap/extension-link'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
@ -327,6 +329,32 @@ const additionalLinkProtocols = [
'notion',
]
const MarkdownPasteHandler = Extension.create({
name: 'markdownPasteHandler',
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey('markdownPasteHandler'),
props: {
handlePaste: (view, event) => {
const text = event.clipboardData?.getData('text/plain')
if (!text) return false
const html = marked.parse(text)
// It is fine to paste the content without sanitizing because it will be sanitized later by TipTap
this.editor.commands.insertContent(html)
// https://github.com/ueberdosis/tiptap/discussions/4118#discussioncomment-8931999
return true
},
},
}),
]
},
})
const extensions : Extensions = [
// Starterkit:
StarterKit.configure({
@ -418,6 +446,8 @@ const extensions : Extensions = [
Commands.configure({
suggestion: suggestionSetup(t),
}),
MarkdownPasteHandler,
]
// Add a custom extension for the Escape key