From bd8a429edbaf224bf15bdabe1df9cad0de580c4e Mon Sep 17 00:00:00 2001 From: rahulnits-sketch Date: Fri, 25 Sep 2026 21:45:35 +0530 Subject: [PATCH 1/2] fix(react): prevent intra-word _ and * from rendering as emphasis --- .../react/src/lib/contentEditableComposer.js | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/react/src/lib/contentEditableComposer.js b/packages/react/src/lib/contentEditableComposer.js index ffa3f6f9c..04f4f379a 100644 --- a/packages/react/src/lib/contentEditableComposer.js +++ b/packages/react/src/lib/contentEditableComposer.js @@ -44,6 +44,26 @@ const findLink = (text, start) => { }; }; +const isAlphanumeric = (ch) => Boolean(ch && /[a-zA-Z0-9]/.test(ch)); +const isWhitespace = (ch) => Boolean(ch && /\s/.test(ch)); + +const isValidEmphasis = (marker, text, start, end) => { + if (marker === '`' || marker === '~~') { + return true; + } + const prevChar = start > 0 ? text[start - 1] : ''; + const firstInside = text[start + marker.length]; + const lastInside = text[end - 1]; + const nextChar = + end + marker.length < text.length ? text[end + marker.length] : ''; + + if (isWhitespace(firstInside) || isWhitespace(lastInside)) return false; + if (isAlphanumeric(prevChar)) return false; + if (isAlphanumeric(nextChar)) return false; + + return true; +}; + const appendMarkdown = (parent, text) => { const tokens = [ ['**', 'strong'], @@ -92,7 +112,10 @@ const appendMarkdown = (parent, text) => { if (token) { const [marker, tagName] = token; const end = text.indexOf(marker, index + marker.length); - if (end > index + marker.length) { + if ( + end > index + marker.length && + isValidEmphasis(marker, text, index, end) + ) { flush(); const formatted = document.createElement(tagName); const content = text.slice(index + marker.length, end); From f162c128a472c9caf32c56dd56875d8ac6bb4560 Mon Sep 17 00:00:00 2001 From: rahulnits-sketch Date: Sun, 4 Oct 2026 23:48:25 +0530 Subject: [PATCH 2/2] fix(react): restrict intra-word check to underscore and scan subsequent closers --- .../react/src/lib/contentEditableComposer.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/react/src/lib/contentEditableComposer.js b/packages/react/src/lib/contentEditableComposer.js index 04f4f379a..03e568aa1 100644 --- a/packages/react/src/lib/contentEditableComposer.js +++ b/packages/react/src/lib/contentEditableComposer.js @@ -58,8 +58,12 @@ const isValidEmphasis = (marker, text, start, end) => { end + marker.length < text.length ? text[end + marker.length] : ''; if (isWhitespace(firstInside) || isWhitespace(lastInside)) return false; - if (isAlphanumeric(prevChar)) return false; - if (isAlphanumeric(nextChar)) return false; + + // CommonMark: only underscore delimiters cannot be intra-word + if (marker === '_' || marker === '__') { + if (isAlphanumeric(prevChar)) return false; + if (isAlphanumeric(nextChar)) return false; + } return true; }; @@ -111,11 +115,11 @@ const appendMarkdown = (parent, text) => { if (token) { const [marker, tagName] = token; - const end = text.indexOf(marker, index + marker.length); - if ( - end > index + marker.length && - isValidEmphasis(marker, text, index, end) - ) { + let end = text.indexOf(marker, index + marker.length); + while (end !== -1 && !isValidEmphasis(marker, text, index, end)) { + end = text.indexOf(marker, end + marker.length); + } + if (end > index + marker.length) { flush(); const formatted = document.createElement(tagName); const content = text.slice(index + marker.length, end);