Repository navigation
Closed
Description
Summary
- In the rendered markdown file view, Cmd+C writes markdown source into the clipboard's text/plain flavor, so a copied inline code span pastes with its backticks
- A copied fenced block pastes with the whole fence, language tag included
- Any target that reads only text/plain shows the syntax: Chrome's find bar or a WhatsApp text input
- The editable view has no clean escape. It skips the in-app context menu, and the native right-click Copy runs the same serializer
Steps to reproduce
- Open a .md file in the desktop app, in the rendered markdown view.
- Select an inline code span, e.g. a function name the source wrote between backticks.
- Press Cmd+C.
- Paste into Chrome's find bar (Cmd+F) or a WhatsApp text input.
Expected
text/plain holds the visible text, the way window.getSelection().toString() returns it. The markdown belongs in text/html, which ProseMirror already sets.
Actual
text/plain holds markdown source. The find bar receives the identifier wrapped in backticks, so the search matches nothing. A fenced block behaves the same way: the paste keeps the opening fence with its language tag, and the closing fence.
Investigation
- The behavior is deliberate.
TableClipboardMarkdown(priority 1000) installs aclipboardTextSerializeratapps/desktop/src/renderer/components/MarkdownRenderer/components/TipTapMarkdownRenderer/createMarkdownExtensions.ts:174. serializeSelectionForClipboard(serializeMarkdownTable.ts:196) routes a CellSelection and any selection inside a table tosliceToPlainText, and everything else to the markdown serializer. Its comment calls that last branch "preserves copy-as-markdown".- The table carve-out added for Issue with Copying Preview-State Table Strings in Markdown #5698 already treats markdown-in-text/plain as the wrong result inside a table. The same reasoning holds outside one.
- Editable mode offers no way out.
TipTapMarkdownRenderer.tsxreturns early, beforeSelectionContextMenu, wheneditableis true, so the right-click Copy that Issue with Copying Preview-State Table Strings in Markdown #5698's triage pointed to is simply absent.FileViewerContent.tsxpasseseditableunconditionally. Right-click then reaches the native Electron menu fromapps/desktop/src/main/lib/edit-context-menu.ts, whosecopyrole fires the same DOM copy event and the same serializer. - The one clean path left is a code block's hover Copy button. It covers fenced blocks and not inline code.
- No setting turns this off. The app defines exactly one
clipboardTextSerializer, it reads no store or option, and the only markdown preference is the page style ("default" | "tufte"). - Options, in my order of preference: (a) put the rendered text in text/plain and leave the markdown in text/html; (b) add a setting, next to the terminal's "copy on select"; (c) add a "Copy as Markdown" action and keep Cmd+C plain.
Environment
Superset 1.28.0, desktop app on macOS. Code paths read from main, which matches the 1.28.0 bundle.
Metadata
Metadata
Assignees
Labels
No labels
Activity
github-actions commentedon Sep 11, 2026
Summary
The report is accurate and the behavior is reproducible from the code: in the rendered markdown file view, the clipboard's
text/plainflavor is deliberately filled with markdown source for any selection that isn't inside a table, so an inline code span pastes as`foo`and a fenced block pastes with its opening fence, language tag, and closing fence.text/htmlis unaffected (ProseMirror writes its own serialized DOM), so rich-text targets look right and only plain-text consumers — Chrome's find bar, a chat input, a terminal — see the syntax. The report's claim that editable mode has no escape hatch also checks out: the in-app context menu is gated behind!editable, and the file viewer passeseditableunconditionally.Affected code
The serializer that produces markdown in
text/plain:apps/desktop/src/renderer/components/MarkdownRenderer/components/TipTapMarkdownRenderer/createMarkdownExtensions.ts:165-196— theTableClipboardMarkdownextension,priority: 1000, installs aclipboardTextSerializerProseMirror prop. The priority is intentional (comment at:159-164) so it is consulted ahead of tiptap-markdown's own serializer.apps/desktop/src/renderer/components/MarkdownRenderer/components/TipTapMarkdownRenderer/serializeMarkdownTable.ts:196-213—serializeSelectionForClipboard. Three branches; the last one,:212, is the bug surface::194describes this as "preserves copy-as-markdown", confirming the report's read that this is deliberate rather than accidental.apps/desktop/src/renderer/components/MarkdownRenderer/components/TipTapMarkdownRenderer/createMarkdownExtensions.ts:287-292—Markdown.configure({ transformCopiedText: true }). This is a second, redundant markdown-to-text/plainpath (tiptap-markdown installs its ownclipboardTextSerializerwhen this flag is set, at extension priority 50). It matters for the fix: deleting or bypassingTableClipboardMarkdownalone would not fix anything, it would just hand the job back to tiptap-markdown and also regress the table carve-out.The missing escape hatch in editable mode:
apps/desktop/src/renderer/components/MarkdownRenderer/components/TipTapMarkdownRenderer/TipTapMarkdownRenderer.tsx:238-246— returnscontentbare wheneditable, and only wraps inSelectionContextMenuotherwise.apps/desktop/src/renderer/components/MarkdownRenderer/components/SelectionContextMenu/SelectionContextMenu.tsx:56-64— the Copy item this skips is exactly the wanted behavior: it copieswindow.getSelection().toString(), bypassing the ProseMirror serializer entirely..../FileViewerPane/components/FileViewerContent/FileViewerContent.tsx:455-462—editableis passed with no condition, so the rendered.mdview always takes the branch without the context menu.apps/desktop/src/main/lib/edit-context-menu.ts:42-51— the native fallback right-click menu uses{ role: "copy" }, which is the Chromium copy path and therefore re-enters the sameclipboardTextSerializer. Nothing there can produce plain text.One useful confirmation signal: the v2 pane makes the same prop conditional —
.../FilePane/registry/views/MarkdownPreviewView/MarkdownPreviewView.tsx:31setseditable = content.length <= MAX_EDITABLE_LENGTH. A markdown file above that threshold renders read-only, regainsSelectionContextMenu, and its right-click Copy should yield clean text while Cmd+C on the same selection still yields markdown. That asymmetry is a cheap manual check of the diagnosis.Root cause
Not a regression or an edge case —
serializeMarkdownTable.ts:212is the designed behavior, and the table carve-out added alongside it (:201-210) is a narrower version of the same complaint: markdown intext/plainwas judged wrong inside a table but left in place everywhere else. The report's observation that the reasoning generalizes is fair; whether to generalize it is a product call, not a code fix waiting to happen.I ruled out a configuration cause.
clipboardTextSerializerappears exactly once in the repo (createMarkdownExtensions.ts:174; the only other hit is thetransformCopiedTextflag at:291and its twin inMarkdownEditor.tsx:333), the plugin reads no store or option, and the only persisted markdown preference is the page style —apps/desktop/src/renderer/stores/markdown-preferences/store.ts:4-9holds"default" | "tufte"and nothing else. So there is no setting a user could have flipped, and none to flip back.Reproduction
Manual, matching the report: open a
.mdfile in the rendered view, select an inline code span, Cmd+C, paste into Cmd+F. Expect the identifier, get backticks.For a maintainer working without a build,
serializeMarkdownTable.test.ts:181-295already exercisesserializeSelectionForClipboarddirectly with a real schema, and a failing case drops in next to"keeps markdown for a selection outside any table"(:257-266— note that test currently asserts the buggy behavior and would need to be rewritten by any fix):A fenced-block equivalent (a
codeBlocknode withlanguage: "ts") assertingexpect(result).not.toContain("```")covers the second half of the report.Fix direction
Option (a) is the smallest change and is well-contained: make the final branch of
serializeSelectionForClipboard(serializeMarkdownTable.ts:212) returnsliceToPlainText(slice), keeping the whole-tableCellSelectionbranch as the single markdown exception, and settransformCopiedText: falseatcreateMarkdownExtensions.ts:290so the redundant tiptap-markdown serializer can't quietly reinstate the old behavior later.transformPastedTextshould staytrue— in-app paste preferstext/html(ProseMirror tags it withdata-pm-slice), so round-tripping a copy inside the app does not depend on the text flavor.Two things worth deciding explicitly rather than inheriting:
sliceToPlainText(:178-180) joins blocks with"\n"; ProseMirror's built-in fallback uses"\n\n";window.getSelection().toString()(whatSelectionContextMenucopies) differs from both. The issue asks for thegetSelection()result, and matching it would make Cmd+C and right-click Copy agree — which seems like the right target, but it is not what a one-line change tosliceToPlainTextgives you.Either way, the editable-mode gap at
TipTapMarkdownRenderer.tsx:238-240is worth closing on its own. One trap if you do:edit-context-menu.ts:10-13documents that Radix'spreventDefaultoncontextmenusuppresses the native Electron menu, so mountingSelectionContextMenuover an editable surface would take away Cut, Paste, and spellcheck suggestions unless those items are added to the Radix menu first.fix(desktop): copy rendered text instead of Markdown (#7454) (#7591)