Summary
A selection holding only text is copied as the characters themselves rather than as Markdown, so text the editor deliberately keeps literal reaches the clipboard as live source. Pasting it elsewhere produces a heading, a link, or emphasis the author never wrote. The save path escapes the same text correctly, so the file and the clipboard disagree about what the document says.
Steps to reproduce
- Open a document containing
\# not a heading.
- Select the paragraph and copy it.
- Paste into another Markdown editor, or anywhere the plain-text flavor is the one used.
Expected behavior
The clipboard's Markdown carries the escapes the save path writes, so text the editor keeps literal stays literal wherever it is pasted. Rendered content, the HTML flavor, and selections that already escape correctly are unaffected.
Actual behavior
Measured against 07f5893 by driving the editor mount used by the plugin tests, copying a whole paragraph:
| Document text |
Saved |
Copied |
# not a heading |
\# not a heading |
# not a heading |
*not emphasis* tail |
\*not emphasis\* tail |
*not emphasis* tail |
[test link](./test.html) tail |
\[test link]\(./test.html) tail |
[test link](./test.html) tail |
A selection that is not text-only escapes correctly. The same paragraph copied together with a second paragraph, or with an emphasized span inside it, yields \# not a heading.
Pasting back into Leafdown keeps the text literal, because the HTML flavor wins there. The defect shows wherever the plain-text flavor is the one that survives.
Related context
Done when
Notes, logs, screenshots
Diagnosis
Milkdown's clipboard plugin installs clipboardTextSerializer, which returns slice.content.textBetween(...) when the slice is pure text and only otherwise builds a document and runs serializer(doc) (@milkdown/plugin-clipboard 7.21.3, lib/index.js). #242's escaping handler is installed on the serializer, so it never runs for a text-only slice. That is why adding a second paragraph or a formatted span to the selection restores the escapes.
Out of scope
- The HTML flavor, which carries the same characters and is parsed as text by the in-app ingress.
- Whether an in-app copy should preserve literalness beyond what Markdown can express, which belongs to the escape-representation spike.
Summary
A selection holding only text is copied as the characters themselves rather than as Markdown, so text the editor deliberately keeps literal reaches the clipboard as live source. Pasting it elsewhere produces a heading, a link, or emphasis the author never wrote. The save path escapes the same text correctly, so the file and the clipboard disagree about what the document says.
Steps to reproduce
\# not a heading.Expected behavior
The clipboard's Markdown carries the escapes the save path writes, so text the editor keeps literal stays literal wherever it is pasted. Rendered content, the HTML flavor, and selections that already escape correctly are unaffected.
Actual behavior
Measured against 07f5893 by driving the editor mount used by the plugin tests, copying a whole paragraph:
# not a heading\# not a heading# not a heading*not emphasis* tail\*not emphasis\* tail*not emphasis* tail[test link](./test.html) tail\[test link]\(./test.html) tail[test link](./test.html) tailA selection that is not text-only escapes correctly. The same paragraph copied together with a second paragraph, or with an emphasized span inside it, yields
\# not a heading.Pasting back into Leafdown keeps the text literal, because the HTML flavor wins there. The defect shows wherever the plain-text flavor is the one that survives.
Related context
Done when
Notes, logs, screenshots
Diagnosis
Milkdown's clipboard plugin installs
clipboardTextSerializer, which returnsslice.content.textBetween(...)when the slice is pure text and only otherwise builds a document and runsserializer(doc)(@milkdown/plugin-clipboard7.21.3,lib/index.js). #242's escaping handler is installed on the serializer, so it never runs for a text-only slice. That is why adding a second paragraph or a formatted span to the selection restores the escapes.Out of scope