diff --git a/packages/layout-engine/pm-adapter/src/converters/math-block.test.ts b/packages/layout-engine/pm-adapter/src/converters/math-block.test.ts index 5720466f5e..bf9d141e13 100644 --- a/packages/layout-engine/pm-adapter/src/converters/math-block.test.ts +++ b/packages/layout-engine/pm-adapter/src/converters/math-block.test.ts @@ -80,4 +80,34 @@ describe('handleMathBlockNode', () => { handleMathBlockNode(makeNode({ textContent: 'b' }) as any, context); expect(blocks[0].id).not.toBe(blocks[1].id); }); + + it('resolves paragraph spacing from paragraphProperties', () => { + const { context, blocks } = makeContext(); + const node = makeNode({ + textContent: 'x', + paragraphProperties: { spacing: { before: 240, after: 160, line: 276, lineRule: 'auto' } }, + }); + + handleMathBlockNode(node as any, context); + + const block = blocks[0] as ParagraphBlock; + expect(block.attrs?.spacing).toBeDefined(); + expect(block.attrs?.spacing?.before).toBeGreaterThan(0); + expect(block.attrs?.spacing?.after).toBeGreaterThan(0); + }); + + it('falls back to paragraph alignment when justification is unknown', () => { + const { context, blocks } = makeContext(); + const node = makeNode({ + textContent: 'x', + justification: 'unknown', + paragraphProperties: { justification: 'right' }, + }); + + handleMathBlockNode(node as any, context); + + const block = blocks[0] as ParagraphBlock; + // When math justification is unknown, falls back to paragraphAttrs alignment + expect(block.attrs?.alignment).toBeDefined(); + }); }); diff --git a/packages/layout-engine/pm-adapter/src/converters/math-block.ts b/packages/layout-engine/pm-adapter/src/converters/math-block.ts index 80f37795ce..1f7dcd176e 100644 --- a/packages/layout-engine/pm-adapter/src/converters/math-block.ts +++ b/packages/layout-engine/pm-adapter/src/converters/math-block.ts @@ -1,6 +1,7 @@ import type { ParagraphBlock, MathRun } from '@superdoc/contracts'; import type { PMNode, NodeHandlerContext } from '../types.js'; import { estimateMathDimensions } from './math-constants.js'; +import { computeParagraphAttrs } from '../attributes/index.js'; const JUSTIFICATION_TO_ALIGN: Record = { center: 'center', @@ -32,12 +33,17 @@ export function handleMathBlockNode(node: PMNode, context: NodeHandlerContext): pmEnd: pos?.end, }; + // Resolve paragraph spacing from the parent w:p's properties (carried via paragraphProperties attr). + // This ensures display math paragraphs get the same spacing as their containing paragraph. + const { paragraphAttrs } = computeParagraphAttrs(node, context.converterContext); + const block: ParagraphBlock = { kind: 'paragraph', id: nextBlockId('paragraph'), runs: [mathRun], attrs: { - alignment: JUSTIFICATION_TO_ALIGN[justification] ?? 'center', + ...paragraphAttrs, + alignment: JUSTIFICATION_TO_ALIGN[justification] ?? paragraphAttrs.alignment ?? 'center', }, }; diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.js b/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.js index ad4debd258..f870c5b5b4 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.js @@ -29,12 +29,15 @@ const handleMathPara = (params) => { const originalXml = carbonCopy(xmlNode); const textContent = extractMathText(xmlNode); const justification = extractJustification(xmlNode); + // Use raw inline properties (not resolved) to match how regular paragraphs work. + // The style-engine resolves them at render time via computeParagraphAttrs. + const paragraphProperties = params.extraParams?.inlineParagraphProperties ?? null; return { nodes: [ { type: 'mathBlock', - attrs: { originalXml, textContent, justification }, + attrs: { originalXml, textContent, justification, paragraphProperties }, marks: [], }, ], diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.test.js b/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.test.js index d971772691..0d9e573556 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.test.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v2/importer/math/math-importer.test.js @@ -99,6 +99,41 @@ describe('mathNodeHandler', () => { const result = handler({ nodes: [oMathParaNode] }); expect(result.nodes[0].attrs.justification).toBe('centerGroup'); }); + + it('captures paragraphProperties from extraParams', () => { + const oMathParaNode = { + name: 'm:oMathPara', + elements: [ + { + name: 'm:oMath', + elements: [{ name: 'm:r', elements: [{ name: 'm:t', elements: [{ type: 'text', text: 'z' }] }] }], + }, + ], + }; + + const spacing = { before: 240, after: 160 }; + const result = handler({ + nodes: [oMathParaNode], + extraParams: { inlineParagraphProperties: { spacing } }, + }); + + expect(result.nodes[0].attrs.paragraphProperties).toEqual({ spacing }); + }); + + it('sets paragraphProperties to null when extraParams is absent', () => { + const oMathParaNode = { + name: 'm:oMathPara', + elements: [ + { + name: 'm:oMath', + elements: [{ name: 'm:r', elements: [{ name: 'm:t', elements: [{ type: 'text', text: 'w' }] }] }], + }, + ], + }; + + const result = handler({ nodes: [oMathParaNode] }); + expect(result.nodes[0].attrs.paragraphProperties).toBeNull(); + }); }); describe('non-math elements', () => { diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/p/helpers/legacy-handle-paragraph-node.js b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/p/helpers/legacy-handle-paragraph-node.js index 40d304294a..796b1e3f75 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/p/helpers/legacy-handle-paragraph-node.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/p/helpers/legacy-handle-paragraph-node.js @@ -147,6 +147,7 @@ export const handleParagraphNode = (params) => { extraParams: { ...params.extraParams, paragraphProperties: resolvedParagraphProperties, + inlineParagraphProperties, numberingDefinedInline: Boolean(inlineParagraphProperties.numberingProperties), }, path: [...(params.path || []), node], diff --git a/packages/super-editor/src/editors/v1/extensions/math/math-block.js b/packages/super-editor/src/editors/v1/extensions/math/math-block.js index 51f4c5a68d..8b4969fbf1 100644 --- a/packages/super-editor/src/editors/v1/extensions/math/math-block.js +++ b/packages/super-editor/src/editors/v1/extensions/math/math-block.js @@ -30,6 +30,10 @@ export const MathBlock = Node.create({ default: 'centerGroup', rendered: false, }, + paragraphProperties: { + default: null, + rendered: false, + }, }; }, });