From a638d1c07e7d23da258932b38291cc2505b4dc2b Mon Sep 17 00:00:00 2001 From: Kim Brown Date: Tue, 5 May 2026 18:39:36 -0400 Subject: [PATCH 1/2] Write: strip lone
placeholders in convertToBlocks() to fix empty block markup Empty headings and blockquotes inserted via slash commands carried a contentEditable
placeholder into Gutenberg block markup, causing block validation failures. Strip the placeholder before the empty-content check so these blocks are cleanly skipped. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../jetpack-mu-wpcom/changelog/fix-write-empty-gb-blocks | 4 ++++ projects/packages/jetpack-mu-wpcom/src/features/write/view.js | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 projects/packages/jetpack-mu-wpcom/changelog/fix-write-empty-gb-blocks diff --git a/projects/packages/jetpack-mu-wpcom/changelog/fix-write-empty-gb-blocks b/projects/packages/jetpack-mu-wpcom/changelog/fix-write-empty-gb-blocks new file mode 100644 index 00000000000..144c86f43cd --- /dev/null +++ b/projects/packages/jetpack-mu-wpcom/changelog/fix-write-empty-gb-blocks @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Write: strip lone br placeholders in convertToBlocks to fix empty heading and blockquote block markup diff --git a/projects/packages/jetpack-mu-wpcom/src/features/write/view.js b/projects/packages/jetpack-mu-wpcom/src/features/write/view.js index bedcd294025..dd584f40983 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/write/view.js +++ b/projects/packages/jetpack-mu-wpcom/src/features/write/view.js @@ -287,7 +287,9 @@ function convertToBlocks( html ) { if ( node.nodeType !== Node.ELEMENT_NODE ) continue; const tag = node.tagName.toLowerCase(); - const inner = node.innerHTML.trim(); + // Strip lone
placeholders left by contentEditable so empty + // blocks are not serialized with stale markup. + const inner = node.innerHTML.trim().replace( /^$/, '' ); if ( ! inner && ! [ 'figure', 'img', 'hr' ].includes( tag ) ) continue; From 4c67f7c1fb9049faedf9261a551fa35b5a5772b3 Mon Sep 17 00:00:00 2001 From: Kim Brown Date: Tue, 5 May 2026 22:58:52 -0400 Subject: [PATCH 2/2] Write: preserve empty headings, blockquotes, and lists in convertToBlocks() Instead of skipping empty blocks entirely, serialize them as valid empty Gutenberg blocks to match how the block editor handles empty content. Empty paragraphs are still skipped as they are typically whitespace noise. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/features/write/view.js | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/projects/packages/jetpack-mu-wpcom/src/features/write/view.js b/projects/packages/jetpack-mu-wpcom/src/features/write/view.js index dd584f40983..e9a0de3a831 100644 --- a/projects/packages/jetpack-mu-wpcom/src/features/write/view.js +++ b/projects/packages/jetpack-mu-wpcom/src/features/write/view.js @@ -291,7 +291,12 @@ function convertToBlocks( html ) { // blocks are not serialized with stale markup. const inner = node.innerHTML.trim().replace( /^$/, '' ); - if ( ! inner && ! [ 'figure', 'img', 'hr' ].includes( tag ) ) continue; + if ( + ! inner && + ! [ 'figure', 'img', 'hr', 'blockquote', 'ul', 'ol' ].includes( tag ) && + ! /^h[1-6]$/.test( tag ) + ) + continue; // Check for text alignment. const align = node.style && node.style.textAlign; @@ -349,11 +354,15 @@ function convertToBlocks( html ) { ); } else if ( tag === 'ul' || tag === 'ol' ) { // Wrap each
  • in wp:list-item block comments. - const listItems = Array.from( node.querySelectorAll( ':scope > li' ) ) - .map( - li => `\n
  • ${ li.innerHTML.trim() }
  • \n` - ) - .join( '\n' ); + const liNodes = Array.from( node.querySelectorAll( ':scope > li' ) ); + const listItems = liNodes.length + ? liNodes + .map( + li => + `\n
  • ${ li.innerHTML.trim() }
  • \n` + ) + .join( '\n' ) + : '\n
  • \n'; const listTag = tag === 'ol' ? 'ol' : 'ul'; const attrs = tag === 'ol' ? ' {"ordered":true}' : ''; blocks.push(