| 1758 | 1758 | |
| 1759 | 1759 | // Replace line breaks with <br> in unknown elements |
| 1760 | 1760 | if (node instanceof HTMLUnknownElement) { |
| 1761 | 1761 | node.innerHTML = node.innerHTML.trim().replaceAll('\n', '<br>'); |
| 1762 | + |
| 1763 | + /** @type {Text[]} */ |
| 1764 | + const candidates = []; |
| 1765 | + const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT); |
| 1766 | + while (walker.nextNode()) { |
| 1767 | + const textNode = /** @type {Text} */ (walker.currentNode); |
| 1768 | + if (!textNode.data.includes('\n')) continue; |
| 1769 | + |
| 1770 | + // Skip if this text node is within a <pre> (any ancestor) |
| 1771 | + if (textNode.parentElement && textNode.parentElement.closest('pre')) continue; |
| 1772 | + |
| 1773 | + candidates.push(textNode); |
| 1774 | + } |
| 1775 | + |
| 1776 | + for (const textNode of candidates) { |
| 1777 | + const parts = textNode.data.split('\n'); |
| 1778 | + const frag = document.createDocumentFragment(); |
| 1779 | + parts.forEach((part, idx) => { |
| 1780 | + if (part.length) { |
| 1781 | + frag.appendChild(document.createTextNode(part)); |
| 1782 | + } |
| 1783 | + if (idx < parts.length - 1) { |
| 1784 | + frag.appendChild(document.createElement('br')); |
| 1785 | + } |
| 1786 | + }); |
| 1787 | + textNode.replaceWith(frag); |
| 1788 | + } |
| 1762 | 1789 | } |
| 1763 | 1790 | |
| 1764 | 1791 | const isMediaAllowed = isExternalMediaAllowed(); |