` and `` elements where whitespace should be preserved.
+ */
+function normalizeTextNodeWhitespace(element: HTMLElement) {
+ const preserveWSTags = new Set(["PRE", "CODE"]);
+ const walker = element.ownerDocument.createTreeWalker(
+ element,
+ // NodeFilter.SHOW_TEXT
+ 4,
+ {
+ acceptNode(node) {
+ // Skip text nodes inside pre/code elements
+ let parent = node.parentElement;
+ while (parent && parent !== element) {
+ if (preserveWSTags.has(parent.tagName)) {
+ // NodeFilter.FILTER_REJECT
+ return 2;
+ }
+ parent = parent.parentElement;
+ }
+ // NodeFilter.FILTER_ACCEPT
+ return 1;
+ },
+ },
+ );
+
+ const textNodes: Text[] = [];
+ let node: Node | null;
+ while ((node = walker.nextNode())) {
+ textNodes.push(node as Text);
+ }
+
+ for (const textNode of textNodes) {
+ if (textNode.nodeValue && /[\r\n]/.test(textNode.nodeValue)) {
+ textNode.nodeValue = textNode.nodeValue.replace(/[ \t\r\n\f]+/g, " ");
+ }
+ }
+}
+
+/**
+ * Normalizes whitespace in HTML text nodes to match standard CSS
+ * white-space:normal behavior. Skipped for Notion HTML which intentionally
+ * uses `\n` for hard breaks.
+ */
+export function preprocessHTMLWhitespace(element: HTMLElement) {
+ if (!isNotionHTML(element)) {
+ normalizeTextNodeWhitespace(element);
+ }
+}
diff --git a/tests/src/unit/core/clipboard/paste/pasteTestInstances.ts b/tests/src/unit/core/clipboard/paste/pasteTestInstances.ts
index cf9e0d33dd..0220d816d9 100644
--- a/tests/src/unit/core/clipboard/paste/pasteTestInstances.ts
+++ b/tests/src/unit/core/clipboard/paste/pasteTestInstances.ts
@@ -1,10 +1,5 @@
import { TextSelection } from "@tiptap/pm/state";
-import {
- TestBlockSchema,
- TestInlineContentSchema,
- TestStyleSchema,
-} from "../../testSchema.js";
import { PasteTestCase } from "../../../shared/clipboard/paste/pasteTestCase.js";
import {
testPasteHTML,
@@ -12,6 +7,11 @@ import {
} from "../../../shared/clipboard/paste/pasteTestExecutors.js";
import { getPosOfTextNode } from "../../../shared/testUtil.js";
import { TestInstance } from "../../../types.js";
+import {
+ TestBlockSchema,
+ TestInlineContentSchema,
+ TestStyleSchema,
+} from "../../testSchema.js";
export const pasteTestInstancesHTML: TestInstance<
PasteTestCase,
diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/mixedTextTableCell.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/mixedTextTableCell.json
index 40018a5ae2..0ee4579333 100644
--- a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/mixedTextTableCell.json
+++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/mixedTextTableCell.json
@@ -15,10 +15,7 @@
{
"styles": {},
"text": "Table Cell
-Table Cell
-
- Table Cell
-",
+Table Cell Table Cell",
"type": "text",
},
],
diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/html/msWordPaste.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/msWordPaste.json
new file mode 100644
index 0000000000..b7eb0a7de4
--- /dev/null
+++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/html/msWordPaste.json
@@ -0,0 +1,126 @@
+[
+ {
+ "children": [],
+ "content": [
+ {
+ "styles": {
+ "bold": true,
+ "underline": true,
+ },
+ "text": "Que se passe-t-il si je réponds tard à un message chat et que l'utilisateur n'est plus en ligne :",
+ "type": "text",
+ },
+ ],
+ "id": "1",
+ "props": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+ {
+ "children": [],
+ "content": [
+ {
+ "styles": {},
+ "text": "Lorsque vous envoyez un message à un utilisateur dans une conversation chat, et qu'il est encore en ligne, il recevra le message sur sa bulle chatbot.",
+ "type": "text",
+ },
+ ],
+ "id": "2",
+ "props": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+ {
+ "children": [],
+ "content": [
+ {
+ "styles": {},
+ "text": "Cependant S'il n'est plus en ligne, votre message sera envoyé par email si :",
+ "type": "text",
+ },
+ ],
+ "id": "3",
+ "props": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+ {
+ "children": [],
+ "content": [
+ {
+ "styles": {},
+ "text": ". l'utilisateur n'a pas lu votre réponse après 2 minutes",
+ "type": "text",
+ },
+ ],
+ "id": "4",
+ "props": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+ {
+ "children": [],
+ "content": [
+ {
+ "styles": {},
+ "text": ". l'utilisateur n'est plus présent sur votre site web",
+ "type": "text",
+ },
+ ],
+ "id": "5",
+ "props": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+ {
+ "children": [],
+ "content": [
+ {
+ "styles": {},
+ "text": " ",
+ "type": "text",
+ },
+ ],
+ "id": "6",
+ "props": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+ {
+ "children": [],
+ "content": [
+ {
+ "styles": {},
+ "text": "Cela se fait automatiquement donc, lorsque nous répondons par chat, si l'utilisateur n'est plus là, Crisp renvoie le message alors par email et le canal de discussion se transforme en canal de discussion email.
+
+ Il est possible aussi de créer une conversation email directement le profil de l'utilisateur (bouton bleu en haut à droite de la conversation)",
+ "type": "text",
+ },
+ ],
+ "id": "7",
+ "props": {
+ "backgroundColor": "default",
+ "textAlignment": "left",
+ "textColor": "default",
+ },
+ "type": "paragraph",
+ },
+]
\ No newline at end of file
diff --git a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts
index d4ca058799..5972397392 100644
--- a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts
+++ b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts
@@ -949,6 +949,70 @@ console.log("Third Line") `,
},
executeTest: testParseHTML,
},
+ {
+ testCase: {
+ name: "msWordPaste",
+ content: `
+
+
+
+
+
+
+
+
+
+
+
+
+Que se passe-t-il si je réponds tard à
+un message chat et que l'utilisateur n'est plus en ligne :
Lorsque vous envoyez un message à un
+utilisateur dans une conversation chat, et qu'il est encore en ligne, il
+recevra le message sur sa bulle chatbot.
Cependant
+S'il n'est plus en ligne, votre message sera envoyé par email si :
.
+l'utilisateur n'a pas lu votre réponse après 2 minutes
.
+l'utilisateur n'est plus présent sur votre site web
Cela se fait automatiquement donc, lorsque
+nous répondons par chat, si l'utilisateur n'est plus là, Crisp renvoie le
+message alors par email et le canal de discussion se transforme en canal de
+discussion email.
+
+Il est possible aussi de créer une conversation email directement le profil de
+l'utilisateur (bouton bleu en haut à droite de la conversation)