diff --git a/packages/lexical-html/src/import/coreImportRules.ts b/packages/lexical-html/src/import/coreImportRules.ts
index 87c4adc6c65..6b6f8b5a2de 100644
--- a/packages/lexical-html/src/import/coreImportRules.ts
+++ b/packages/lexical-html/src/import/coreImportRules.ts
@@ -27,6 +27,7 @@ import {
IS_SUBSCRIPT,
IS_SUPERSCRIPT,
IS_UNDERLINE,
+ isAppleInterchangeNewline,
isBlockDomNode,
isDOMTextNode,
isLastChildInBlockNode,
@@ -453,7 +454,8 @@ const LineBreakRule = /* @__PURE__ */ defineImportRule({
// would otherwise survive as a LineBreakNode and tack an extra blank
// line onto the imported content.
$import: (_ctx, el) =>
- isOnlyChildInBlockNode(el) || isLastChildInBlockNode(el)
+ isOnlyChildInBlockNode(el) ||
+ (isAppleInterchangeNewline(el) && isLastChildInBlockNode(el))
? []
: [$createLineBreakNode()],
match: sel.tag('br'),
diff --git a/packages/lexical-utils/src/__tests__/unit/LexicalEventHelpers.test.tsx b/packages/lexical-utils/src/__tests__/unit/LexicalEventHelpers.test.tsx
index 4ba20a998a0..56693485852 100644
--- a/packages/lexical-utils/src/__tests__/unit/LexicalEventHelpers.test.tsx
+++ b/packages/lexical-utils/src/__tests__/unit/LexicalEventHelpers.test.tsx
@@ -737,9 +737,9 @@ describe('LexicalEventHelpers', () => {
},
{
expectedHTML:
- '
1
2
3
',
+ '1
2
3
',
inputs: [pasteHTML('12
3')],
- name: 'last br in a block node is ignored',
+ name: 'last br in a block node is preserved',
},
];
diff --git a/packages/lexical/src/index.ts b/packages/lexical/src/index.ts
index 282400a275e..7b87a99202f 100644
--- a/packages/lexical/src/index.ts
+++ b/packages/lexical/src/index.ts
@@ -386,6 +386,7 @@ export type {SerializedLineBreakNode} from './nodes/LexicalLineBreakNode';
export {
$createLineBreakNode,
$isLineBreakNode,
+ isAppleInterchangeNewline,
isLastChildInBlockNode,
isOnlyChildInBlockNode,
LineBreakNode,
diff --git a/packages/lexical/src/nodes/LexicalLineBreakNode.ts b/packages/lexical/src/nodes/LexicalLineBreakNode.ts
index 376ae273ad7..3e90e83dfc5 100644
--- a/packages/lexical/src/nodes/LexicalLineBreakNode.ts
+++ b/packages/lexical/src/nodes/LexicalLineBreakNode.ts
@@ -19,6 +19,7 @@ import {
$applyNodeReplacement,
isBlockDomNode,
isDOMTextNode,
+ isHTMLElement,
} from '../LexicalUtils';
export type SerializedLineBreakNode = SerializedLexicalNode;
@@ -58,7 +59,10 @@ export class LineBreakNode extends LexicalNode {
static importDOM(): DOMConversionMap | null {
return {
br: (node: Node) => {
- if (isOnlyChildInBlockNode(node) || isLastChildInBlockNode(node)) {
+ if (
+ isOnlyChildInBlockNode(node) ||
+ (isAppleInterchangeNewline(node) && isLastChildInBlockNode(node))
+ ) {
return null;
}
return {
@@ -151,6 +155,14 @@ export function isLastChildInBlockNode(node: Node): boolean {
return false;
}
+export function isAppleInterchangeNewline(node: Node): boolean {
+ return (
+ node.nodeName === 'BR' &&
+ isHTMLElement(node) &&
+ node.getAttribute('class') === 'Apple-interchange-newline'
+ );
+}
+
function isWhitespaceDomTextNode(node: Node): boolean {
return isDOMTextNode(node) && /^( |\t|\r?\n)+$/.test(node.textContent || '');
}