diff --git a/shared/editor/marks/Code.ts b/shared/editor/marks/Code.ts index 9ac12087b..03d418a34 100644 --- a/shared/editor/marks/Code.ts +++ b/shared/editor/marks/Code.ts @@ -40,8 +40,8 @@ export default class Code extends Mark { get schema(): MarkSpec { return { excludes: "_", - parseDOM: [{ tag: "code", preserveWhitespace: true }], - toDOM: () => ["code", { spellCheck: "false" }], + parseDOM: [{ tag: "code.inline", preserveWhitespace: true }], + toDOM: () => ["code", { class: "inline", spellCheck: "false" }], }; } diff --git a/shared/editor/nodes/Blockquote.ts b/shared/editor/nodes/Blockquote.ts index 7f560df86..3787ee656 100644 --- a/shared/editor/nodes/Blockquote.ts +++ b/shared/editor/nodes/Blockquote.ts @@ -16,7 +16,11 @@ export default class Blockquote extends Node { content: "block+", group: "block", defining: true, - parseDOM: [{ tag: "blockquote" }], + parseDOM: [ + { tag: "blockquote" }, + // Dropbox Paper parsing, yes their quotes are actually lists + { tag: "ul.listtype-quote", contentElement: "li" }, + ], toDOM: () => ["blockquote", 0], }; } diff --git a/shared/editor/nodes/CodeFence.ts b/shared/editor/nodes/CodeFence.ts index cc5d59682..140662c58 100644 --- a/shared/editor/nodes/CodeFence.ts +++ b/shared/editor/nodes/CodeFence.ts @@ -89,6 +89,7 @@ export default class CodeFence extends Node { defining: true, draggable: false, parseDOM: [ + { tag: "code" }, { tag: "pre", preserveWhitespace: "full" }, { tag: ".code-block", diff --git a/shared/editor/plugins/PasteHandler.ts b/shared/editor/plugins/PasteHandler.ts index 1376cddeb..dfcc54433 100644 --- a/shared/editor/plugins/PasteHandler.ts +++ b/shared/editor/plugins/PasteHandler.ts @@ -7,6 +7,12 @@ import isUrl from "../lib/isUrl"; import selectionIsInCode from "../queries/isInCode"; import { LANGUAGES } from "./Prism"; +function isDropboxPaper(html: string): boolean { + // The best we have to detect if a paste is likely coming from Paper + // In this case it's actually better to use the text version + return html?.includes("usually-unique-id"); +} + /** * Add support for additional syntax that users paste even though it isn't * supported by the markdown parser directly by massaging the text content. @@ -14,13 +20,16 @@ import { LANGUAGES } from "./Prism"; * @param text The incoming pasted plain text */ function normalizePastedMarkdown(text: string): string { - // find checkboxes not contained in a list and wrap them in list items const CHECKBOX_REGEX = /^\s?(\[(X|\s|_|-)\]\s(.*)?)/gim; + // find checkboxes not contained in a list and wrap them in list items while (text.match(CHECKBOX_REGEX)) { text = text.replace(CHECKBOX_REGEX, (match) => `- ${match.trim()}`); } + // find multiple newlines and insert a hard break to ensure they are respected + text = text.replace(/\n{2,}/g, "\n\n\\\n"); + return text; } @@ -33,6 +42,13 @@ export default class PasteHandler extends Extension { return [ new Plugin({ props: { + transformPastedHTML(html: string) { + if (isDropboxPaper(html)) { + // Fixes double paragraphs when pasting from Dropbox Paper + html = html.replace(/

<\/div>/gi, "

"); + } + return html; + }, handlePaste: (view, event: ClipboardEvent) => { if (view.props.editable && !view.props.editable(view.state)) { return false; @@ -125,7 +141,7 @@ export default class PasteHandler extends Extension { // If the text on the clipboard looks like Markdown OR there is no // html on the clipboard then try to parse content as Markdown if ( - isMarkdown(text) || + (isMarkdown(text) && !isDropboxPaper(html)) || html.length === 0 || pasteCodeLanguage === "markdown" ) {