From c6e3898ef1fb54222be70130d42d7cb49193e524 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Mon, 30 Mar 2026 11:31:27 -0300 Subject: [PATCH 1/2] fix(super-converter): add null guards to decodeRPrFromMarks for sparse marks Tracked format snapshots can contain sparse marks (missing attrs) or null entries. Without guards, mark.type.name and mark.attrs throw. --- .../editors/v1/core/super-converter/styles.js | 20 ++++++++++--------- .../v1/core/super-converter/styles.test.js | 12 +++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/super-editor/src/editors/v1/core/super-converter/styles.js b/packages/super-editor/src/editors/v1/core/super-converter/styles.js index 54305523ad..8d504b4a08 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/styles.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/styles.js @@ -517,16 +517,18 @@ export function decodeRPrFromMarks(marks) { } marks.forEach((mark) => { - const type = mark.type.name ?? mark.type; + if (!mark) return; + const type = mark.type?.name ?? mark.type; + if (!type) return; + const attrs = mark.attrs || {}; switch (type) { case 'strike': case 'italic': case 'bold': - runProperties[type] = mark.attrs.value !== '0' && mark.attrs.value !== false; + runProperties[type] = attrs.value !== '0' && attrs.value !== false; break; case 'underline': { - const { underlineType, underlineColor, underlineThemeColor, underlineThemeTint, underlineThemeShade } = - mark.attrs; + const { underlineType, underlineColor, underlineThemeColor, underlineThemeTint, underlineThemeShade } = attrs; const underlineAttrs = {}; if (underlineType) { underlineAttrs['w:val'] = underlineType; @@ -549,11 +551,11 @@ export function decodeRPrFromMarks(marks) { break; } case 'highlight': - if (mark.attrs.color) { - if (mark.attrs.color.toLowerCase() === 'transparent') { + if (attrs.color) { + if (attrs.color.toLowerCase() === 'transparent') { runProperties.highlight = { 'w:val': 'none' }; } else { - runProperties.highlight = { 'w:val': mark.attrs.color }; + runProperties.highlight = { 'w:val': attrs.color }; } } break; @@ -561,8 +563,8 @@ export function decodeRPrFromMarks(marks) { runProperties.styleId = 'Hyperlink'; break; case 'textStyle': - Object.keys(mark.attrs).forEach((attr) => { - const value = mark.attrs[attr]; + Object.keys(attrs).forEach((attr) => { + const value = attrs[attr]; switch (attr) { case 'textTransform': if (value != null) { diff --git a/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js b/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js index e46295acf0..0ecdc662db 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js @@ -444,6 +444,18 @@ describe('decodeRPrFromMarks', () => { expect(rPr).toEqual({ color: { val: 'FF0000' }, fontSize: 24 }); }); + it('should handle sparse mark snapshots with missing attrs', () => { + const marks = [{ type: 'bold' }, { type: 'italic' }, { type: 'textStyle' }]; + const rPr = decodeRPrFromMarks(marks); + expect(rPr).toEqual({ bold: true, italic: true }); + }); + + it('should skip null or undefined marks without throwing', () => { + const marks = [null, { type: 'bold', attrs: { value: true } }, undefined]; + const rPr = decodeRPrFromMarks(marks); + expect(rPr).toEqual({ bold: true }); + }); + it('should decode underline marks', () => { const marks = [{ type: 'underline', attrs: { underlineType: 'single', underlineColor: '#FF0000' } }]; const rPr = decodeRPrFromMarks(marks); From cb6ce21bd0610cf3b238d9a4510be0744a816c79 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Mon, 30 Mar 2026 11:42:21 -0300 Subject: [PATCH 2/2] test: add coverage for marks with missing or falsy type --- .../src/editors/v1/core/super-converter/styles.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js b/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js index 0ecdc662db..105e695825 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/styles.test.js @@ -456,6 +456,12 @@ describe('decodeRPrFromMarks', () => { expect(rPr).toEqual({ bold: true }); }); + it('should skip marks with missing or falsy type', () => { + const marks = [{ type: null }, { attrs: { value: true } }, { type: 'bold', attrs: { value: true } }]; + const rPr = decodeRPrFromMarks(marks); + expect(rPr).toEqual({ bold: true }); + }); + it('should decode underline marks', () => { const marks = [{ type: 'underline', attrs: { underlineType: 'single', underlineColor: '#FF0000' } }]; const rPr = decodeRPrFromMarks(marks);