From b5924ffa993196b6df1d121519bc54917f973651 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Fri, 3 Apr 2026 11:17:03 +0800 Subject: [PATCH] fix(editor): reliable markdown paste and inline code line spacing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Markdown paste: replace heuristic-based detection with a single deterministic check — only use HTML clipboard path when source is another ProseMirror editor (identified by data-pm-slice attribute). All other pastes (VS Code, text editors, terminals, .md files) parse text/plain as Markdown via @tiptap/markdown. Inline code: add box-decoration-break: clone and line-height: 2 so multi-line inline code renders with proper spacing between lines. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/web/features/editor/content-editor.css | 3 ++ .../editor/extensions/markdown-paste.ts | 34 +++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/apps/web/features/editor/content-editor.css b/apps/web/features/editor/content-editor.css index b6e7ed16..697771a7 100644 --- a/apps/web/features/editor/content-editor.css +++ b/apps/web/features/editor/content-editor.css @@ -125,6 +125,9 @@ color: color-mix(in srgb, var(--foreground) 75%, transparent); padding: 0.125rem 0.375rem; border-radius: var(--radius-sm); + box-decoration-break: clone; + -webkit-box-decoration-break: clone; + line-height: 2; } /* Code blocks */ diff --git a/apps/web/features/editor/extensions/markdown-paste.ts b/apps/web/features/editor/extensions/markdown-paste.ts index fc549e8f..7dde1fb9 100644 --- a/apps/web/features/editor/extensions/markdown-paste.ts +++ b/apps/web/features/editor/extensions/markdown-paste.ts @@ -11,17 +11,29 @@ export function createMarkdownPasteExtension() { new Plugin({ key: new PluginKey("markdownPaste"), props: { - clipboardTextParser(text, _context, plainText) { - if (!plainText && editor.markdown) { - const json = editor.markdown.parse(text); - const node = editor.schema.nodeFromJSON(json); - return Slice.maxOpen(node.content); - } - // Plain text fallback - const p = editor.schema.nodes.paragraph!; - const doc = editor.schema.nodes.doc!; - const paragraph = p.create(null, text ? editor.schema.text(text) : undefined); - return new Slice(doc.create(null, paragraph).content, 0, 0); + handlePaste(view, event) { + if (!editor.markdown) return false; + const clipboard = event.clipboardData; + if (!clipboard) return false; + + const text = clipboard.getData("text/plain"); + if (!text) return false; + + const html = clipboard.getData("text/html"); + + // If HTML contains data-pm-slice, the source is another + // ProseMirror editor — let ProseMirror use its native HTML + // clipboard path to preserve exact node structure. + if (html && html.includes("data-pm-slice")) return false; + + // Everything else (VS Code, text editors, .md files, terminals, + // web pages): parse text/plain as Markdown. + const json = editor.markdown.parse(text); + const node = editor.schema.nodeFromJSON(json); + const slice = Slice.maxOpen(node.content); + const tr = view.state.tr.replaceSelection(slice); + view.dispatch(tr); + return true; }, }, }),