Skip to content

Commit c255558

Browse files
nperez0111claude
andauthored
fix: backslash newlines when copying from a code block (#2709)
* fix: backslash newlines when copying from a code block When copying inline content from inside a code block, the text/plain clipboard payload had a backslash before every newline (markdown's hard-break syntax) and the text/html had `<br>` separators inside the code instead of literal newlines. Two changes fix this: - copyExtension routes selections inside a code block through the block-export path so the code block's own toExternalHTML produces the proper `<pre><code>` wrapper. - serializeInlineContentExternalHTML now plumbs blockType through to inlineContentToNodes (mirroring the internal HTML serializer) so `\n` in code-block content stays as literal text instead of being split into hardBreak nodes that render as `<br>`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: drop `as const` from PRETTIFY_OPTIONS to satisfy htmlfy types `as const` typed `ignore` as a readonly tuple, which doesn't match `UserConfig.ignore: string[]`, breaking CI typecheck. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: integrate code-block copy regression into existing copy test suite Address PR review: - Drop standalone codeBlockMarkdown.test.ts; add `codeBlockFullContent` and `codeBlockPartialSelection` cases to copyTestInstances.ts and snapshot text/plain markdown for all copy test instances via a new `Copy tests (Markdown)` describe block (mirrors the export test pattern). - Trim verbose comments in copyExtension and serializeBlocksExternalHTML. - copyPasteEquality executor now passes the actual markdown payload as the text/plain MIME instead of a literal "text" placeholder, so paste handlers that prefer text/plain (e.g. inside code blocks) round-trip correctly. - Update mixedInParagraph snapshots: prettify's `ignore: ["code"]` preserves trailing whitespace inside `<code>` spans, which is the actual HTML output. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8a03b69 commit c255558

33 files changed

Lines changed: 234 additions & 27 deletions

packages/core/src/api/clipboard/toClipboard/copyExtension.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,18 @@ export function selectedFragmentToHTML<
140140
editor,
141141
);
142142

143-
const markdown = cleanHTMLToMarkdown(externalHTML);
143+
// Code blocks are treated differently for copying: text/plain is the raw
144+
// selected text instead of markdown.
145+
const { $from, $to } = view.state.selection;
146+
const parentBlockType = $from.parent.type.name;
147+
const parentBlockSpec = editor.blockImplementations[parentBlockType as any];
148+
const isPurelyInsideCodeBlock =
149+
$from.sameParent($to) &&
150+
parentBlockSpec?.implementation.meta?.code === true;
151+
152+
const markdown = isPurelyInsideCodeBlock
153+
? view.state.doc.textBetween($from.pos, $to.pos)
154+
: cleanHTMLToMarkdown(externalHTML);
144155

145156
return { clipboardHTML, externalHTML, markdown };
146157
}

packages/core/src/api/exporters/html/util/serializeBlocksExternalHTML.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,25 @@ export function serializeInlineContentExternalHTML<
3737
editor: BlockNoteEditor<any, I, S>,
3838
blockContent: PartialBlock<BSchema, I, S>["content"],
3939
serializer: DOMSerializer,
40-
options?: { document?: Document },
40+
options?: { document?: Document; blockType?: string },
4141
) {
4242
let nodes: Node[];
4343

4444
// TODO: reuse function from nodeconversions?
4545
if (!blockContent) {
4646
throw new Error("blockContent is required");
4747
} else if (typeof blockContent === "string") {
48-
nodes = inlineContentToNodes([blockContent], editor.pmSchema);
48+
nodes = inlineContentToNodes(
49+
[blockContent],
50+
editor.pmSchema,
51+
options?.blockType,
52+
);
4953
} else if (Array.isArray(blockContent)) {
50-
nodes = inlineContentToNodes(blockContent, editor.pmSchema);
54+
nodes = inlineContentToNodes(
55+
blockContent,
56+
editor.pmSchema,
57+
options?.blockType,
58+
);
5159
} else if (blockContent.type === "tableContent") {
5260
nodes = tableContentToNodes(blockContent, editor.pmSchema);
5361
} else {
@@ -262,7 +270,7 @@ function serializeBlock<
262270
editor,
263271
block.content as any, // TODO
264272
serializer,
265-
options,
273+
{ ...options, blockType: block.type },
266274
);
267275

268276
ret.contentDOM.appendChild(ic);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const a = 1;
2+
<br />
3+
const b = 2;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
onst a = 1;
2+
<br />
3+
const b = 2
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Paragraph 1
2+
3+
# Heading 1
4+
5+
1. Numbered List Item 1
6+
7+
* Bullet List Item 1
8+
* [ ] Check List Item 1
9+
* Toggle List Item 1
10+
11+
```text
12+
console.log("Hello World");
13+
```
14+
15+
| | |
16+
| ------------ | ------------ |
17+
| Table Cell 1 | Table Cell 2 |
18+
| Table Cell 3 | Table Cell 4 |
19+
20+
21+
22+
***
23+
24+
Paragraph 2
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Paragraph 1
2+
3+
## Heading 1
4+
5+
2. Numbered List Item 1
6+
7+
* Bullet List Item 1
8+
* [x] Check List Item 1
9+
* Toggle List Item 1
10+
11+
```typescript
12+
console.log("Hello World");
13+
```
14+
15+
| | |
16+
| ------------ | ------------ |
17+
| Table Cell 1 | Table Cell 2 |
18+
| Table Cell 3 | Table Cell 4 |
19+
20+
<figure><img alt="1280px-Placeholder_view_vector.svg.png" src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Placeholder_view_vector.svg/1280px-Placeholder_view_vector.svg.png"><figcaption>Placeholder</figcaption></figure>
21+
22+
***
23+
24+
Paragraph 2
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Paragraph 1
2+
3+
Nested Paragraph 1
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Nested Paragraph 1
2+
3+
Nested Paragraph 2
4+
5+
Nested Paragraph 3
6+
7+
Paragraph 2
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Nested Paragraph 1
2+
3+
Nested Paragraph 2
4+
5+
Nested Paragraph 3
6+
7+
Paragraph 2
8+
9+
Nested Paragraph 4
10+
11+
Nested Paragraph 5
12+
13+
Nested Paragraph 6
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const a = 1;
2+
const b = 2;

0 commit comments

Comments
 (0)