diff --git a/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.js b/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.js index b3c27fc395..762d5af6e1 100644 --- a/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.js +++ b/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.js @@ -250,24 +250,32 @@ export function bodySectPrShouldEmbed(bodySectPr) { return !!(cols?.count && cols.count > 1); } -/** Embeds PM slice, media, and optional body sectPr as hidden base64 payloads. */ +function hiddenClipboardPayload(attr, base64) { + return `
`; +} + +function readClipboardPayload(el, attr) { + return el.getAttribute(attr)?.trim() || el.textContent?.trim() || ''; +} + +/** Embeds PM slice, media, and optional body sectPr as hidden base64 payload attributes. */ export function embedSliceInHtml(html, sliceJson, bodySectPrJson = '', mediaJson = '') { let out = html; if (bodySectPrJson) { const body64 = encodeUtf8Base64(bodySectPrJson); - out = `
${body64}
${out}`; + out = `${hiddenClipboardPayload(SUPERDOC_BODY_SECT_PR_ATTR, body64)}${out}`; } if (mediaJson) { const media64 = encodeUtf8Base64(mediaJson); - out = `
${media64}
${out}`; + out = `${hiddenClipboardPayload(SUPERDOC_MEDIA_ATTR, media64)}${out}`; } if (!sliceJson) return out; const base64 = encodeUtf8Base64(sliceJson); - return `
${base64}
${out}`; + return `${hiddenClipboardPayload(SUPERDOC_SLICE_ATTR, base64)}${out}`; } /** - * Reads slice JSON from HTML produced by {@link embedSliceInHtml} (hidden div + base64 text). + * Reads slice JSON from HTML produced by {@link embedSliceInHtml}. */ export function extractSliceFromHtml(html) { if (!html || !html.includes(SUPERDOC_SLICE_ATTR)) return null; @@ -278,10 +286,7 @@ export function extractSliceFromHtml(html) { const el = doc.querySelector(`[${SUPERDOC_SLICE_ATTR}]`); if (!el) return null; - let b64 = el.textContent?.trim() ?? ''; - if (!b64) { - b64 = el.getAttribute(SUPERDOC_SLICE_ATTR)?.trim() ?? ''; - } + const b64 = readClipboardPayload(el, SUPERDOC_SLICE_ATTR); if (!b64) return null; const decoded = decodeUtf8Base64(b64); @@ -314,7 +319,7 @@ export function extractMediaFromHtml(html) { const doc = new DOMParser().parseFromString(html, 'text/html'); const el = doc.querySelector(`[${SUPERDOC_MEDIA_ATTR}]`); if (!el) return null; - const b64 = el.textContent?.trim() ?? ''; + const b64 = readClipboardPayload(el, SUPERDOC_MEDIA_ATTR); if (!b64) return null; const decoded = decodeUtf8Base64(b64); return decoded || null; @@ -331,7 +336,7 @@ export function extractBodySectPrFromHtml(html) { const doc = new DOMParser().parseFromString(html, 'text/html'); const el = doc.querySelector(`[${SUPERDOC_BODY_SECT_PR_ATTR}]`); if (!el) return null; - const b64 = el.textContent?.trim() ?? ''; + const b64 = readClipboardPayload(el, SUPERDOC_BODY_SECT_PR_ATTR); if (!b64) return null; return JSON.parse(decodeUtf8Base64(b64)); } catch { diff --git a/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.test.js b/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.test.js index c2352c1989..8c2ee547a8 100644 --- a/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.test.js +++ b/packages/super-editor/src/editors/v1/core/helpers/superdocClipboardSlice.test.js @@ -248,6 +248,25 @@ describe('HTML slice embed/extract round-trip', () => { expect(extracted).toBe(sampleSlice); }); + it('stores embedded payloads in attributes instead of hidden text nodes', () => { + const mediaJson = JSON.stringify({ 'word/media/image1.png': 'data:image/png;base64,AAA' }); + const embedded = embedSliceInHtml(sampleHtml, sampleSlice, '', mediaJson); + + expect(embedded).toMatch(/
<\/div>/); + expect(embedded).toMatch(/
<\/div>/); + expect(embedded).not.toMatch(/]*data-superdoc-slice[^>]*>[^<]+<\/div>/); + expect(embedded).not.toMatch(/]*data-sd-superdoc-media[^>]*>[^<]+<\/div>/); + }); + + it('extractSliceFromHtml still supports older hidden text payloads', () => { + const embedded = embedSliceInHtml(sampleHtml, sampleSlice); + const payload = embedded.match(/data-superdoc-slice="([^"]+)"/)?.[1]; + expect(payload).toBeTruthy(); + + const oldFormatHtml = `
${payload}
${sampleHtml}`; + expect(extractSliceFromHtml(oldFormatHtml)).toBe(sampleSlice); + }); + it('extractMediaFromHtml recovers embedded media JSON', () => { const mediaJson = JSON.stringify({ 'word/media/image1.png': 'data:image/png;base64,AAA' }); const embedded = embedSliceInHtml(sampleHtml, sampleSlice, '', mediaJson);