From ed7a40967a34a8867bebd8b12611a8a2637b3b7e Mon Sep 17 00:00:00 2001 From: Steffen Becker Date: Sun, 16 Aug 2026 07:46:34 +0200 Subject: [PATCH] fix: an empty block no longer steals the next block's minted style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The whole-block regexes in the sentinel passes (page breaks, paragraph styles/boxes, text effects, whitespace collapse) matched a self-closing too — [^>]* happily ends in a slash — and then swallowed everything up to the next real closing tag, so an empty cell paragraph took the restyle meant for a later paragraph's sentinel and that paragraph lost it. The opening-tag pattern now rejects self-closing tags. --- src/lib/export/odt.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/export/odt.ts b/src/lib/export/odt.ts index 2149c20..9eebd44 100644 --- a/src/lib/export/odt.ts +++ b/src/lib/export/odt.ts @@ -1596,7 +1596,7 @@ function applyPageBreaks(odtBytes: Uint8Array): Uint8Array { // The sentinel sits anywhere in the block: a custom paragraph's serialized text // starts with the FSZ/STY/PBX payloads (their passes run later), then the PGB. content = content.replace( - new RegExp(`]*)>([\\s\\S]*?)`, 'g'), + new RegExp(`]*[^/>])?)>([\\s\\S]*?)`, 'g'), (m, tag: string, attrs: string, inner: string) => { if (!inner.includes(PGB)) return m; const sm = /text:style-name="([^"]*)"/.exec(attrs); @@ -1933,7 +1933,7 @@ function applyParagraphStyles(odtBytes: Uint8Array): Uint8Array { const styRe = new RegExp(`${STY}([^${STY}]*)${STY}`); content = content.replace( - new RegExp(`]*)>([\\s\\S]*?)`, 'g'), + new RegExp(`]*[^/>])?)>([\\s\\S]*?)`, 'g'), (m, tag: string, attrs: string, inner: string) => { const sm = styRe.exec(inner); if (!sm) return m; @@ -1991,7 +1991,7 @@ function applyParagraphBoxes(odtBytes: Uint8Array): Uint8Array { const pbxRe = new RegExp(`${PBX}([^${PBX}]*)${PBX}`); content = content.replace( - new RegExp(`]*)>([\\s\\S]*?)`, 'g'), + new RegExp(`]*[^/>])?)>([\\s\\S]*?)`, 'g'), (m, tag: string, attrs: string, inner: string) => { const sm = pbxRe.exec(inner); if (!sm) return m; @@ -3123,7 +3123,7 @@ function applyCharacterStyles(odtBytes: Uint8Array): Uint8Array { const cstRe = new RegExp(`${CST}([^${CST}]*)${CST}`); content = content.replace( - new RegExp(`]*)>([\\s\\S]*?)`, 'g'), + new RegExp(`]*[^/>])?)>([\\s\\S]*?)`, 'g'), (m, attrs: string, inner: string) => { const sm = cstRe.exec(inner); if (!sm) return m; @@ -3172,7 +3172,7 @@ function resolveTextEffects(xml: string, mint: (styleXml: string) => void, prefi const tefRe = new RegExp(`${TEF}([^${TEF}]*)${TEF}`); let out = xml.replace( - new RegExp(`]*)>([\\s\\S]*?)`, 'g'), + new RegExp(`]*[^/>])?)>([\\s\\S]*?)`, 'g'), (m, attrs: string, inner: string) => { const sm = tefRe.exec(inner); if (!sm) return m; @@ -3571,7 +3571,7 @@ function collapseRunWhitespace(odtBytes: Uint8Array): Uint8Array { let content = strFromU8(contentBytes); content = content.replace( - /]*>[\s\S]*?<\/text:\1>/g, + /]*[^/>])?>[\s\S]*?<\/text:\1>/g, (block) => block.replace(/\n/g, ''), );