@chayns-components/emoji-input 5.5.65 → 5.5.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,6 +32,9 @@ const serializeSpanElementToText = element => {
32
32
  if (childNodes.length === 0) {
33
33
  return BLOCK_SEPARATOR;
34
34
  }
35
+ if (childNodes.length === 1 && isBreakElement(childNodes[0])) {
36
+ return BLOCK_SEPARATOR;
37
+ }
35
38
  return serializeHTMLToText(childNodes);
36
39
  };
37
40
  const serializeHTMLNodeToText = node => {
@@ -55,6 +58,9 @@ const serializeHTMLNodeToText = node => {
55
58
  };
56
59
  const serializeHTMLToText = nodes => {
57
60
  const serializedNodes = Array.from(nodes);
61
+ if (serializedNodes.length === 1 && isBreakElement(serializedNodes[0])) {
62
+ return '';
63
+ }
58
64
  let result = '';
59
65
  let didSerializeBlockElement = false;
60
66
  serializedNodes.forEach(node => {
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":["_regex","require","_emoji","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","_node$textContent","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeSpanElementToText","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","exports","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","unescapeHTML","replace","HTML_A_TAG_REGEX","BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","_","prefix","type","value","entity","prefixAttr","BB_IGNORE_EMOJI_REGEX","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","container","escapeHTML","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeSpanElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return BLOCK_SEPARATOR;\n }\n\n return serializeHTMLToText(childNodes);\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (element.tagName === 'SPAN') {\n return serializeSpanElementToText(element);\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/&nbsp;/g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"text.js","names":["_regex","require","_emoji","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","_node$textContent","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeSpanElementToText","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","exports","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","unescapeHTML","replace","HTML_A_TAG_REGEX","BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","_","prefix","type","value","entity","prefixAttr","BB_IGNORE_EMOJI_REGEX","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","container","escapeHTML","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeSpanElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return BLOCK_SEPARATOR;\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return BLOCK_SEPARATOR;\n }\n\n return serializeHTMLToText(childNodes);\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (element.tagName === 'SPAN') {\n return serializeSpanElementToText(element);\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n if (serializedNodes.length === 1 && isBreakElement(serializedNodes[0]!)) {\n return '';\n }\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/&nbsp;/g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
@@ -17,6 +17,7 @@ const serializeHTML = html => {
17
17
  (0, _vitest.expect)(result.match(/<br>/g) ?? []).toHaveLength(1);
18
18
  });
19
19
  (0, _vitest.it)('preserves line breaks, nesting, and block structure inside spans', () => {
20
+ (0, _vitest.expect)(serializeHTML('<br>')).toBe('');
20
21
  (0, _vitest.expect)(serializeHTML('<span><br></span>')).toBe('<br>');
21
22
  (0, _vitest.expect)(serializeHTML('<span>before<span>nested</span>after</span>')).toBe('beforenestedafter');
22
23
  (0, _vitest.expect)(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');
@@ -1 +1 @@
1
- {"version":3,"file":"text.test.js","names":["_vitest","require","_text","serializeHTML","html","container","document","createElement","innerHTML","serializeHTMLToText","childNodes","describe","it","result","expect","toBe","not","toContain","match","toHaveLength","convertHTMLToText","preserveSpaces"],"sources":["../../../src/utils/text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { convertHTMLToText, serializeHTMLToText } from './text';\n\nconst serializeHTML = (html: string) => {\n const container = document.createElement('div');\n container.innerHTML = html;\n\n return serializeHTMLToText(container.childNodes);\n};\n\ndescribe('serializeHTMLToText', () => {\n it('normalizes Safari styled spans without losing an empty line', () => {\n const result = serializeHTML(\n 'question?<span style=\"font-size: var(--base-font-size, 15px);\"></span><span style=\"font-size: var(--base-font-size, 15px);\">Unsere Logs…</span>',\n );\n\n expect(result).toBe('question?<br>Unsere Logs…');\n expect(result).not.toContain('<span');\n expect(result).not.toContain('</span>');\n expect(result).not.toContain('style=');\n expect(result.match(/<br>/g) ?? []).toHaveLength(1);\n });\n\n it('preserves line breaks, nesting, and block structure inside spans', () => {\n expect(serializeHTML('<span><br></span>')).toBe('<br>');\n expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe(\n 'beforenestedafter',\n );\n expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');\n });\n\n it('keeps semantic replacements and unsupported-element fallback behavior', () => {\n expect(\n convertHTMLToText('<span><span class=\"no-emoji-convert\">:smile:</span></span>', {\n preserveSpaces: true,\n }),\n ).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');\n expect(\n convertHTMLToText(\n '<span><lc_mention id=\"user\"><span>@</span>Jane</lc_mention></span>',\n { preserveSpaces: true },\n ),\n ).toBe('[lc_mention id=\"user\"]Jane[/lc_mention]');\n expect(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');\n });\n});\n"],"mappings":";;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,KAAA,GAAAD,OAAA;AAEA,MAAME,aAAa,GAAIC,IAAY,IAAK;EACpC,MAAMC,SAAS,GAAGC,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC/CF,SAAS,CAACG,SAAS,GAAGJ,IAAI;EAE1B,OAAO,IAAAK,yBAAmB,EAACJ,SAAS,CAACK,UAAU,CAAC;AACpD,CAAC;AAED,IAAAC,gBAAQ,EAAC,qBAAqB,EAAE,MAAM;EAClC,IAAAC,UAAE,EAAC,6DAA6D,EAAE,MAAM;IACpE,MAAMC,MAAM,GAAGV,aAAa,CACxB,iJACJ,CAAC;IAED,IAAAW,cAAM,EAACD,MAAM,CAAC,CAACE,IAAI,CAAC,2BAA2B,CAAC;IAChD,IAAAD,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,OAAO,CAAC;IACrC,IAAAH,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,SAAS,CAAC;IACvC,IAAAH,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,QAAQ,CAAC;IACtC,IAAAH,cAAM,EAACD,MAAM,CAACK,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAACC,YAAY,CAAC,CAAC,CAAC;EACvD,CAAC,CAAC;EAEF,IAAAP,UAAE,EAAC,kEAAkE,EAAE,MAAM;IACzE,IAAAE,cAAM,EAACX,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAACY,IAAI,CAAC,MAAM,CAAC;IACvD,IAAAD,cAAM,EAACX,aAAa,CAAC,6CAA6C,CAAC,CAAC,CAACY,IAAI,CACrE,mBACJ,CAAC;IACD,IAAAD,cAAM,EAACX,aAAa,CAAC,4CAA4C,CAAC,CAAC,CAACY,IAAI,CAAC,iBAAiB,CAAC;EAC/F,CAAC,CAAC;EAEF,IAAAH,UAAE,EAAC,uEAAuE,EAAE,MAAM;IAC9E,IAAAE,cAAM,EACF,IAAAM,uBAAiB,EAAC,4DAA4D,EAAE;MAC5EC,cAAc,EAAE;IACpB,CAAC,CACL,CAAC,CAACN,IAAI,CAAC,oCAAoC,CAAC;IAC5C,IAAAD,cAAM,EACF,IAAAM,uBAAiB,EACb,oEAAoE,EACpE;MAAEC,cAAc,EAAE;IAAK,CAC3B,CACJ,CAAC,CAACN,IAAI,CAAC,yCAAyC,CAAC;IACjD,IAAAD,cAAM,EAACX,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAACY,IAAI,CAAC,0BAA0B,CAAC;EACtF,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"text.test.js","names":["_vitest","require","_text","serializeHTML","html","container","document","createElement","innerHTML","serializeHTMLToText","childNodes","describe","it","result","expect","toBe","not","toContain","match","toHaveLength","convertHTMLToText","preserveSpaces"],"sources":["../../../src/utils/text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { convertHTMLToText, serializeHTMLToText } from './text';\n\nconst serializeHTML = (html: string) => {\n const container = document.createElement('div');\n container.innerHTML = html;\n\n return serializeHTMLToText(container.childNodes);\n};\n\ndescribe('serializeHTMLToText', () => {\n it('normalizes Safari styled spans without losing an empty line', () => {\n const result = serializeHTML(\n 'question?<span style=\"font-size: var(--base-font-size, 15px);\"></span><span style=\"font-size: var(--base-font-size, 15px);\">Unsere Logs…</span>',\n );\n\n expect(result).toBe('question?<br>Unsere Logs…');\n expect(result).not.toContain('<span');\n expect(result).not.toContain('</span>');\n expect(result).not.toContain('style=');\n expect(result.match(/<br>/g) ?? []).toHaveLength(1);\n });\n\n it('preserves line breaks, nesting, and block structure inside spans', () => {\n expect(serializeHTML('<br>')).toBe('');\n expect(serializeHTML('<span><br></span>')).toBe('<br>');\n expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe(\n 'beforenestedafter',\n );\n expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');\n });\n\n it('keeps semantic replacements and unsupported-element fallback behavior', () => {\n expect(\n convertHTMLToText('<span><span class=\"no-emoji-convert\">:smile:</span></span>', {\n preserveSpaces: true,\n }),\n ).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');\n expect(\n convertHTMLToText(\n '<span><lc_mention id=\"user\"><span>@</span>Jane</lc_mention></span>',\n { preserveSpaces: true },\n ),\n ).toBe('[lc_mention id=\"user\"]Jane[/lc_mention]');\n expect(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');\n });\n});\n"],"mappings":";;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,KAAA,GAAAD,OAAA;AAEA,MAAME,aAAa,GAAIC,IAAY,IAAK;EACpC,MAAMC,SAAS,GAAGC,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC/CF,SAAS,CAACG,SAAS,GAAGJ,IAAI;EAE1B,OAAO,IAAAK,yBAAmB,EAACJ,SAAS,CAACK,UAAU,CAAC;AACpD,CAAC;AAED,IAAAC,gBAAQ,EAAC,qBAAqB,EAAE,MAAM;EAClC,IAAAC,UAAE,EAAC,6DAA6D,EAAE,MAAM;IACpE,MAAMC,MAAM,GAAGV,aAAa,CACxB,iJACJ,CAAC;IAED,IAAAW,cAAM,EAACD,MAAM,CAAC,CAACE,IAAI,CAAC,2BAA2B,CAAC;IAChD,IAAAD,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,OAAO,CAAC;IACrC,IAAAH,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,SAAS,CAAC;IACvC,IAAAH,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,QAAQ,CAAC;IACtC,IAAAH,cAAM,EAACD,MAAM,CAACK,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAACC,YAAY,CAAC,CAAC,CAAC;EACvD,CAAC,CAAC;EAEF,IAAAP,UAAE,EAAC,kEAAkE,EAAE,MAAM;IACzE,IAAAE,cAAM,EAACX,aAAa,CAAC,MAAM,CAAC,CAAC,CAACY,IAAI,CAAC,EAAE,CAAC;IACtC,IAAAD,cAAM,EAACX,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAACY,IAAI,CAAC,MAAM,CAAC;IACvD,IAAAD,cAAM,EAACX,aAAa,CAAC,6CAA6C,CAAC,CAAC,CAACY,IAAI,CACrE,mBACJ,CAAC;IACD,IAAAD,cAAM,EAACX,aAAa,CAAC,4CAA4C,CAAC,CAAC,CAACY,IAAI,CAAC,iBAAiB,CAAC;EAC/F,CAAC,CAAC;EAEF,IAAAH,UAAE,EAAC,uEAAuE,EAAE,MAAM;IAC9E,IAAAE,cAAM,EACF,IAAAM,uBAAiB,EAAC,4DAA4D,EAAE;MAC5EC,cAAc,EAAE;IACpB,CAAC,CACL,CAAC,CAACN,IAAI,CAAC,oCAAoC,CAAC;IAC5C,IAAAD,cAAM,EACF,IAAAM,uBAAiB,EACb,oEAAoE,EACpE;MAAEC,cAAc,EAAE;IAAK,CAC3B,CACJ,CAAC,CAACN,IAAI,CAAC,yCAAyC,CAAC;IACjD,IAAAD,cAAM,EAACX,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAACY,IAAI,CAAC,0BAA0B,CAAC;EACtF,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
@@ -23,6 +23,9 @@ const serializeSpanElementToText = element => {
23
23
  if (childNodes.length === 0) {
24
24
  return BLOCK_SEPARATOR;
25
25
  }
26
+ if (childNodes.length === 1 && isBreakElement(childNodes[0])) {
27
+ return BLOCK_SEPARATOR;
28
+ }
26
29
  return serializeHTMLToText(childNodes);
27
30
  };
28
31
  const serializeHTMLNodeToText = node => {
@@ -46,6 +49,9 @@ const serializeHTMLNodeToText = node => {
46
49
  };
47
50
  export const serializeHTMLToText = nodes => {
48
51
  const serializedNodes = Array.from(nodes);
52
+ if (serializedNodes.length === 1 && isBreakElement(serializedNodes[0])) {
53
+ return '';
54
+ }
49
55
  let result = '';
50
56
  let didSerializeBlockElement = false;
51
57
  serializedNodes.forEach(node => {
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":["BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","BB_IGNORE_EMOJI_REGEX","HTML_A_TAG_REGEX","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","escapeHTML","unescapeHTML","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeSpanElementToText","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","replace","_","prefix","type","value","entity","prefixAttr","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","container","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeSpanElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return BLOCK_SEPARATOR;\n }\n\n return serializeHTMLToText(childNodes);\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (element.tagName === 'SPAN') {\n return serializeSpanElementToText(element);\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/&nbsp;/g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"text.js","names":["BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","BB_IGNORE_EMOJI_REGEX","HTML_A_TAG_REGEX","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","escapeHTML","unescapeHTML","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeSpanElementToText","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","replace","_","prefix","type","value","entity","prefixAttr","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","container","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeSpanElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return BLOCK_SEPARATOR;\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return BLOCK_SEPARATOR;\n }\n\n return serializeHTMLToText(childNodes);\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (element.tagName === 'SPAN') {\n return serializeSpanElementToText(element);\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n if (serializedNodes.length === 1 && isBreakElement(serializedNodes[0]!)) {\n return '';\n }\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/&nbsp;/g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
@@ -15,6 +15,7 @@ describe('serializeHTMLToText', () => {
15
15
  expect(result.match(/<br>/g) ?? []).toHaveLength(1);
16
16
  });
17
17
  it('preserves line breaks, nesting, and block structure inside spans', () => {
18
+ expect(serializeHTML('<br>')).toBe('');
18
19
  expect(serializeHTML('<span><br></span>')).toBe('<br>');
19
20
  expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe('beforenestedafter');
20
21
  expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');
@@ -1 +1 @@
1
- {"version":3,"file":"text.test.js","names":["describe","expect","it","convertHTMLToText","serializeHTMLToText","serializeHTML","html","container","document","createElement","innerHTML","childNodes","result","toBe","not","toContain","match","toHaveLength","preserveSpaces"],"sources":["../../../src/utils/text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { convertHTMLToText, serializeHTMLToText } from './text';\n\nconst serializeHTML = (html: string) => {\n const container = document.createElement('div');\n container.innerHTML = html;\n\n return serializeHTMLToText(container.childNodes);\n};\n\ndescribe('serializeHTMLToText', () => {\n it('normalizes Safari styled spans without losing an empty line', () => {\n const result = serializeHTML(\n 'question?<span style=\"font-size: var(--base-font-size, 15px);\"></span><span style=\"font-size: var(--base-font-size, 15px);\">Unsere Logs…</span>',\n );\n\n expect(result).toBe('question?<br>Unsere Logs…');\n expect(result).not.toContain('<span');\n expect(result).not.toContain('</span>');\n expect(result).not.toContain('style=');\n expect(result.match(/<br>/g) ?? []).toHaveLength(1);\n });\n\n it('preserves line breaks, nesting, and block structure inside spans', () => {\n expect(serializeHTML('<span><br></span>')).toBe('<br>');\n expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe(\n 'beforenestedafter',\n );\n expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');\n });\n\n it('keeps semantic replacements and unsupported-element fallback behavior', () => {\n expect(\n convertHTMLToText('<span><span class=\"no-emoji-convert\">:smile:</span></span>', {\n preserveSpaces: true,\n }),\n ).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');\n expect(\n convertHTMLToText(\n '<span><lc_mention id=\"user\"><span>@</span>Jane</lc_mention></span>',\n { preserveSpaces: true },\n ),\n ).toBe('[lc_mention id=\"user\"]Jane[/lc_mention]');\n expect(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');\n });\n});\n"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAC7C,SAASC,iBAAiB,EAAEC,mBAAmB,QAAQ,QAAQ;AAE/D,MAAMC,aAAa,GAAIC,IAAY,IAAK;EACpC,MAAMC,SAAS,GAAGC,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC/CF,SAAS,CAACG,SAAS,GAAGJ,IAAI;EAE1B,OAAOF,mBAAmB,CAACG,SAAS,CAACI,UAAU,CAAC;AACpD,CAAC;AAEDX,QAAQ,CAAC,qBAAqB,EAAE,MAAM;EAClCE,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACpE,MAAMU,MAAM,GAAGP,aAAa,CACxB,iJACJ,CAAC;IAEDJ,MAAM,CAACW,MAAM,CAAC,CAACC,IAAI,CAAC,2BAA2B,CAAC;IAChDZ,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,OAAO,CAAC;IACrCd,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,SAAS,CAAC;IACvCd,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,QAAQ,CAAC;IACtCd,MAAM,CAACW,MAAM,CAACI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAACC,YAAY,CAAC,CAAC,CAAC;EACvD,CAAC,CAAC;EAEFf,EAAE,CAAC,kEAAkE,EAAE,MAAM;IACzED,MAAM,CAACI,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAACQ,IAAI,CAAC,MAAM,CAAC;IACvDZ,MAAM,CAACI,aAAa,CAAC,6CAA6C,CAAC,CAAC,CAACQ,IAAI,CACrE,mBACJ,CAAC;IACDZ,MAAM,CAACI,aAAa,CAAC,4CAA4C,CAAC,CAAC,CAACQ,IAAI,CAAC,iBAAiB,CAAC;EAC/F,CAAC,CAAC;EAEFX,EAAE,CAAC,uEAAuE,EAAE,MAAM;IAC9ED,MAAM,CACFE,iBAAiB,CAAC,4DAA4D,EAAE;MAC5Ee,cAAc,EAAE;IACpB,CAAC,CACL,CAAC,CAACL,IAAI,CAAC,oCAAoC,CAAC;IAC5CZ,MAAM,CACFE,iBAAiB,CACb,oEAAoE,EACpE;MAAEe,cAAc,EAAE;IAAK,CAC3B,CACJ,CAAC,CAACL,IAAI,CAAC,yCAAyC,CAAC;IACjDZ,MAAM,CAACI,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAACQ,IAAI,CAAC,0BAA0B,CAAC;EACtF,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"text.test.js","names":["describe","expect","it","convertHTMLToText","serializeHTMLToText","serializeHTML","html","container","document","createElement","innerHTML","childNodes","result","toBe","not","toContain","match","toHaveLength","preserveSpaces"],"sources":["../../../src/utils/text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { convertHTMLToText, serializeHTMLToText } from './text';\n\nconst serializeHTML = (html: string) => {\n const container = document.createElement('div');\n container.innerHTML = html;\n\n return serializeHTMLToText(container.childNodes);\n};\n\ndescribe('serializeHTMLToText', () => {\n it('normalizes Safari styled spans without losing an empty line', () => {\n const result = serializeHTML(\n 'question?<span style=\"font-size: var(--base-font-size, 15px);\"></span><span style=\"font-size: var(--base-font-size, 15px);\">Unsere Logs…</span>',\n );\n\n expect(result).toBe('question?<br>Unsere Logs…');\n expect(result).not.toContain('<span');\n expect(result).not.toContain('</span>');\n expect(result).not.toContain('style=');\n expect(result.match(/<br>/g) ?? []).toHaveLength(1);\n });\n\n it('preserves line breaks, nesting, and block structure inside spans', () => {\n expect(serializeHTML('<br>')).toBe('');\n expect(serializeHTML('<span><br></span>')).toBe('<br>');\n expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe(\n 'beforenestedafter',\n );\n expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');\n });\n\n it('keeps semantic replacements and unsupported-element fallback behavior', () => {\n expect(\n convertHTMLToText('<span><span class=\"no-emoji-convert\">:smile:</span></span>', {\n preserveSpaces: true,\n }),\n ).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');\n expect(\n convertHTMLToText(\n '<span><lc_mention id=\"user\"><span>@</span>Jane</lc_mention></span>',\n { preserveSpaces: true },\n ),\n ).toBe('[lc_mention id=\"user\"]Jane[/lc_mention]');\n expect(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');\n });\n});\n"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAC7C,SAASC,iBAAiB,EAAEC,mBAAmB,QAAQ,QAAQ;AAE/D,MAAMC,aAAa,GAAIC,IAAY,IAAK;EACpC,MAAMC,SAAS,GAAGC,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC/CF,SAAS,CAACG,SAAS,GAAGJ,IAAI;EAE1B,OAAOF,mBAAmB,CAACG,SAAS,CAACI,UAAU,CAAC;AACpD,CAAC;AAEDX,QAAQ,CAAC,qBAAqB,EAAE,MAAM;EAClCE,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACpE,MAAMU,MAAM,GAAGP,aAAa,CACxB,iJACJ,CAAC;IAEDJ,MAAM,CAACW,MAAM,CAAC,CAACC,IAAI,CAAC,2BAA2B,CAAC;IAChDZ,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,OAAO,CAAC;IACrCd,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,SAAS,CAAC;IACvCd,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,QAAQ,CAAC;IACtCd,MAAM,CAACW,MAAM,CAACI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAACC,YAAY,CAAC,CAAC,CAAC;EACvD,CAAC,CAAC;EAEFf,EAAE,CAAC,kEAAkE,EAAE,MAAM;IACzED,MAAM,CAACI,aAAa,CAAC,MAAM,CAAC,CAAC,CAACQ,IAAI,CAAC,EAAE,CAAC;IACtCZ,MAAM,CAACI,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAACQ,IAAI,CAAC,MAAM,CAAC;IACvDZ,MAAM,CAACI,aAAa,CAAC,6CAA6C,CAAC,CAAC,CAACQ,IAAI,CACrE,mBACJ,CAAC;IACDZ,MAAM,CAACI,aAAa,CAAC,4CAA4C,CAAC,CAAC,CAACQ,IAAI,CAAC,iBAAiB,CAAC;EAC/F,CAAC,CAAC;EAEFX,EAAE,CAAC,uEAAuE,EAAE,MAAM;IAC9ED,MAAM,CACFE,iBAAiB,CAAC,4DAA4D,EAAE;MAC5Ee,cAAc,EAAE;IACpB,CAAC,CACL,CAAC,CAACL,IAAI,CAAC,oCAAoC,CAAC;IAC5CZ,MAAM,CACFE,iBAAiB,CACb,oEAAoE,EACpE;MAAEe,cAAc,EAAE;IAAK,CAC3B,CACJ,CAAC,CAACL,IAAI,CAAC,yCAAyC,CAAC;IACjDZ,MAAM,CAACI,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAACQ,IAAI,CAAC,0BAA0B,CAAC;EACtF,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chayns-components/emoji-input",
3
- "version": "5.5.65",
3
+ "version": "5.5.67",
4
4
  "description": "Input field that supports HTML elements and emojis",
5
5
  "sideEffects": false,
6
6
  "browserslist": [
@@ -75,7 +75,7 @@
75
75
  "vitest": "^1.6.1"
76
76
  },
77
77
  "dependencies": {
78
- "@chayns-components/core": "^5.5.65",
78
+ "@chayns-components/core": "^5.5.67",
79
79
  "@chayns/textstrings": "^1.0.1",
80
80
  "emojilib": "^3.0.12",
81
81
  "react-compiler-runtime": "^1.0.0",
@@ -91,5 +91,5 @@
91
91
  "publishConfig": {
92
92
  "access": "public"
93
93
  },
94
- "gitHead": "39ef43bfeaf3326c8c3f5e24e6f456544313af4c"
94
+ "gitHead": "6b2535f41e5426840bb99fd0669c301103723cbf"
95
95
  }