diff --git a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js index 75513db8a7..a3da2e605b 100644 --- a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js +++ b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.js @@ -6,7 +6,6 @@ import { calculateResolvedParagraphProperties, getResolvedParagraphProperties, } from '@extensions/paragraph/resolvedPropertiesCache.js'; -import { carbonCopy } from '@core/utilities/carbonCopy'; import { collectChangedRangesThroughTransactions } from '@utils/rangeUtils.js'; const RUN_PROPERTIES_DERIVED_FROM_MARKS = new Set([ @@ -74,8 +73,6 @@ export const calculateInlineRunPropertiesPlugin = (editor) => if (!runPositions.size) return null; const selectionPreserver = createSelectionPreserver(tr, newState.selection); - const firstRunPosByParagraph = new Map(); - const sortedRunPositions = Array.from(runPositions).sort((a, b) => b - a); sortedRunPositions.forEach((pos) => { @@ -97,26 +94,6 @@ export const calculateInlineRunPropertiesPlugin = (editor) => ); const runProperties = firstInlineProps ?? null; - let firstRunPos = firstRunPosByParagraph.get(paragraphPos); - if (firstRunPos === undefined) { - firstRunPos = findFirstRunPosInParagraph(paragraphNode, paragraphPos, runType); - firstRunPosByParagraph.set(paragraphPos, firstRunPos); - } - const isFirstInParagraph = firstRunPos === mappedPos; - - if (isFirstInParagraph) { - // Keep paragraph's default runProperties in sync for the first run. - const currentParagraphRunProperties = paragraphNode.attrs?.paragraphProperties?.runProperties ?? null; - if (!areRunPropertiesEqual(currentParagraphRunProperties, runProperties)) { - const inlineParagraphProperties = carbonCopy(paragraphNode.attrs.paragraphProperties) || {}; - inlineParagraphProperties.runProperties = runProperties; - tr.setNodeMarkup(paragraphPos, paragraphNode.type, { - ...paragraphNode.attrs, - paragraphProperties: inlineParagraphProperties, - }); - } - } - if (segments.length === 1) { if (JSON.stringify(runProperties) === JSON.stringify(runNode.attrs.runProperties)) return; tr.setNodeMarkup(mappedPos, runNode.type, { ...runNode.attrs, runProperties }, runNode.marks); @@ -224,24 +201,6 @@ export function extractTableInfo($pos, depth) { return fallbackInfo; } } -/** - * Find the absolute document position of the first run node inside a paragraph. - * - * @param {import('prosemirror-model').Node} paragraphNode - * @param {number} paragraphPos Absolute position of the paragraph node. - * @param {import('prosemirror-model').NodeType} runType - * @returns {number|null} - */ -function findFirstRunPosInParagraph(paragraphNode, paragraphPos, runType) { - let firstRunPos = null; - paragraphNode.descendants((child, childPos) => { - if (firstRunPos !== null) return false; - if (child.type !== runType) return true; - firstRunPos = paragraphPos + 1 + childPos; - return false; - }); - return firstRunPos; -} /** * Split a run node into segments whose inline runProperties match for adjacent content. @@ -409,17 +368,6 @@ function stableStringifyInlineProps(inlineProps) { return JSON.stringify(sorted); } -/** - * Compare two runProperties objects with stable key ordering. - * - * @param {Record|null} left - * @param {Record|null} right - * @returns {boolean} - */ -function areRunPropertiesEqual(left, right) { - return stableStringifyInlineProps(left) === stableStringifyInlineProps(right); -} - /** * Track and reapply selection across run replacements. * diff --git a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js index c39c9ee1bd..f4d792304b 100644 --- a/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js +++ b/packages/super-editor/src/extensions/run/calculateInlineRunPropertiesPlugin.test.js @@ -318,7 +318,7 @@ describe('calculateInlineRunPropertiesPlugin', () => { }); }); - it('keeps paragraph runProperties in sync with the first run', () => { + it('does not sync paragraph runProperties with the first run', () => { const schema = makeSchema(); const doc = paragraphDoc(schema); const state = createState(schema, doc); @@ -327,74 +327,44 @@ describe('calculateInlineRunPropertiesPlugin', () => { const tr = state.tr.addMark(from, to, schema.marks.bold.create()); const { state: nextState } = state.applyTransaction(tr); - const paragraph = nextState.doc.firstChild; - expect(paragraph.attrs.paragraphProperties).toEqual({ runProperties: { bold: true } }); - }); - - it('treats the first run inside inline wrappers as the paragraph first run', () => { - const schema = makeSchema(); - const doc = schema.node('doc', null, [ - schema.node('paragraph', null, [ - schema.node('bookmarkStart', null, [schema.node('run', null, schema.text('Wrapped'))]), - ]), - ]); - const state = createState(schema, doc); - const [wrappedRunPos] = runPositions(state.doc); - const from = wrappedRunPos + 1; - const to = wrappedRunPos + 3; - - const tr = state.tr.addMark(from, to, schema.marks.bold.create()); - const { state: nextState } = state.applyTransaction(tr); - - const paragraph = nextState.doc.firstChild; - expect(paragraph.attrs.paragraphProperties).toEqual({ runProperties: { bold: true } }); - }); - - it('does not update paragraph runProperties when a non-first run changes', () => { - const schema = makeSchema(); - const doc = schema.node('doc', null, [ - schema.node('paragraph', null, [ - schema.node('run', null, schema.text('First')), - schema.node('run', null, schema.text('Second')), - ]), - ]); - const state = createState(schema, doc); - const [firstRunPos, secondRunPos] = runPositions(state.doc); - const from = secondRunPos + 1; - const to = secondRunPos + 3; - - const tr = state.tr.addMark(from, to, schema.marks.bold.create()); - const { state: nextState } = state.applyTransaction(tr); - const paragraph = nextState.doc.firstChild; expect(paragraph.attrs.paragraphProperties).toBeNull(); - const firstRun = nextState.doc.nodeAt(firstRunPos); - expect(firstRun?.attrs.runProperties).toBeNull(); }); - it('updates paragraph runProperties when first run is nested inside an inline container', () => { + it("does not update a paragraph's runProperties using the run's properties", () => { const schema = makeSchema(); + const paragraphRunProperties = { italic: true, styleId: 'ParagraphDefault' }; const doc = schema.node('doc', null, [ - schema.node('paragraph', null, [ - schema.node('pageReference', { instruction: 'PAGEREF _Toc123456789 h' }, [ - schema.node('run', null, schema.text('Ref')), - ]), - schema.node('run', null, schema.text(' tail')), - ]), + schema.node( + 'paragraph', + { + paragraphProperties: { + alignment: 'center', + runProperties: paragraphRunProperties, + }, + }, + [schema.node('run', null, schema.text('Hello')), schema.node('run', null, schema.text('World'))], + ), ]); const state = createState(schema, doc); - const [nestedRunPos] = runPositions(state.doc); - const from = nestedRunPos + 1; - const to = nestedRunPos + 4; + const [firstRunPos] = runPositions(state.doc); + const { from, to } = runTextRangeAtPos(firstRunPos, 0, 2); const tr = state.tr.addMark(from, to, schema.marks.bold.create()); const { state: nextState } = state.applyTransaction(tr); const paragraph = nextState.doc.firstChild; - expect(paragraph.attrs.paragraphProperties).toEqual({ runProperties: { bold: true } }); + expect(paragraph.attrs.paragraphProperties).toEqual({ + alignment: 'center', + runProperties: paragraphRunProperties, + }); + + const updatedRuns = runPositions(nextState.doc); + const updatedRun = nextState.doc.nodeAt(updatedRuns[0]); + expect(updatedRun?.attrs.runProperties).toEqual({ bold: true }); }); - it('does not update paragraph runProperties when nested run is not first in paragraph', () => { + it('does not update paragraph runProperties when a nested run changes', () => { const schema = makeSchema(); const doc = schema.node('doc', null, [ schema.node('paragraph', null, [ diff --git a/packages/super-editor/src/extensions/tests/headless.test.js b/packages/super-editor/src/extensions/tests/headless.test.js index 77a5b0a1d1..c01013ab80 100644 --- a/packages/super-editor/src/extensions/tests/headless.test.js +++ b/packages/super-editor/src/extensions/tests/headless.test.js @@ -166,7 +166,7 @@ describe('Headless Mode Optimization', () => { editor.destroy(); }); - it('updates paragraph runProperties for first runs nested in inline wrappers in headless mode', async () => { + it('does not sync paragraph runProperties for first runs nested in inline wrappers in headless mode', async () => { const buffer = await getTestDataAsFileBuffer('blank-doc.docx'); const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -206,7 +206,7 @@ describe('Headless Mode Optimization', () => { editor.dispatch(tr); const updatedParagraph = editor.state.doc.firstChild; - expect(updatedParagraph?.attrs?.paragraphProperties).toEqual({ runProperties: { bold: true } }); + expect(updatedParagraph?.attrs?.paragraphProperties).toBeNull(); expect(hasInvalidParagraphRangeError(logSpy.mock.calls)).toBe(false); } finally { logSpy.mockRestore();