diff --git a/packages/layout-engine/painters/dom/src/renderer.ts b/packages/layout-engine/painters/dom/src/renderer.ts
index a3ea285900..c9162c54f6 100644
--- a/packages/layout-engine/painters/dom/src/renderer.ts
+++ b/packages/layout-engine/painters/dom/src/renderer.ts
@@ -4739,8 +4739,9 @@ export class DomPainter {
wrapper.className = 'sd-math';
wrapper.style.display = 'inline-block';
wrapper.style.verticalAlign = 'middle';
- wrapper.style.width = `${run.width}px`;
- wrapper.style.height = `${run.height}px`;
+ // Let browser auto-size to MathML content; estimated dimensions are for layout only
+ wrapper.style.minWidth = `${run.width}px`;
+ wrapper.style.minHeight = `${run.height}px`;
wrapper.dataset.layoutEpoch = String(this.layoutEpoch ?? 0);
const mathEl = convertOmmlToMathml(run.ommlJson, this.doc);
diff --git a/packages/super-editor/src/editors/v1/core/presentation-editor/tests/DomSelectionGeometry.test.ts b/packages/super-editor/src/editors/v1/core/presentation-editor/tests/DomSelectionGeometry.test.ts
index 9e92d7487d..8ad6c28777 100644
--- a/packages/super-editor/src/editors/v1/core/presentation-editor/tests/DomSelectionGeometry.test.ts
+++ b/packages/super-editor/src/editors/v1/core/presentation-editor/tests/DomSelectionGeometry.test.ts
@@ -1545,6 +1545,34 @@ describe('computeDomCaretPageLocal', () => {
y: 20,
});
});
+
+ it('positions caret at right edge for non-text nodes when pos equals pmEnd', () => {
+ painterHost.innerHTML = `
+
+
+
![]()
+
+
+ `;
+
+ domPositionIndex.rebuild(painterHost);
+
+ const pageEl = painterHost.querySelector('.superdoc-page') as HTMLElement;
+ const imgEl = painterHost.querySelector('img') as HTMLElement;
+
+ pageEl.getBoundingClientRect = vi.fn(() => createRect(0, 0, 612, 792));
+ imgEl.getBoundingClientRect = vi.fn(() => createRect(10, 20, 100, 100));
+
+ const options = createCaretOptions();
+ const caret = computeDomCaretPageLocal(options, 2);
+
+ expect(caret).not.toBe(null);
+ expect(caret).toMatchObject({
+ pageIndex: 0,
+ x: 110, // elRect.right (10 + 100) - pageRect.left (0)
+ y: 20,
+ });
+ });
});
describe('index rebuild for disconnected elements', () => {
diff --git a/packages/super-editor/src/editors/v1/dom-observer/DomSelectionGeometry.ts b/packages/super-editor/src/editors/v1/dom-observer/DomSelectionGeometry.ts
index 8ac8822084..81ccc2d3a4 100644
--- a/packages/super-editor/src/editors/v1/dom-observer/DomSelectionGeometry.ts
+++ b/packages/super-editor/src/editors/v1/dom-observer/DomSelectionGeometry.ts
@@ -580,9 +580,12 @@ export function computeDomCaretPageLocal(
const textNode = targetEl.firstChild;
if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
const elRect = targetEl.getBoundingClientRect();
+ // For non-text elements (images, math), position caret at the right edge
+ // when pos matches pmEnd (cursor after the element)
+ const atEnd = pos >= entry.pmEnd;
return {
pageIndex: Number(page.dataset.pageIndex ?? '0'),
- x: (elRect.left - pageRect.left) / zoom,
+ x: ((atEnd ? elRect.right : elRect.left) - pageRect.left) / zoom,
y: (elRect.top - pageRect.top) / zoom,
};
}
diff --git a/tests/behavior/tests/importing/fixtures/math-all-objects.docx b/tests/behavior/tests/importing/fixtures/math-all-objects.docx
new file mode 100644
index 0000000000..22c2791447
Binary files /dev/null and b/tests/behavior/tests/importing/fixtures/math-all-objects.docx differ
diff --git a/tests/behavior/tests/importing/math-equations.spec.ts b/tests/behavior/tests/importing/math-equations.spec.ts
new file mode 100644
index 0000000000..c3a38cc440
--- /dev/null
+++ b/tests/behavior/tests/importing/math-equations.spec.ts
@@ -0,0 +1,119 @@
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { test, expect } from '../../fixtures/superdoc.js';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ALL_OBJECTS_DOC = path.resolve(__dirname, 'fixtures/math-all-objects.docx');
+// Single-object test docs are used for focused verification by community contributors.
+// The all-objects doc is used for behavior tests since it exercises the full pipeline.
+
+test.use({ config: { toolbar: 'none', comments: 'off' } });
+
+test.describe('math equation import and rendering', () => {
+ test('imports inline and block math nodes from docx', async ({ superdoc }) => {
+ await superdoc.loadDocument(ALL_OBJECTS_DOC);
+ await superdoc.waitForStable();
+
+ // Verify math nodes exist in the PM document
+ const mathNodeCount = await superdoc.page.evaluate(() => {
+ const view = (window as any).editor?.view;
+ if (!view) return 0;
+ let count = 0;
+ view.state.doc.descendants((node: any) => {
+ if (node.type.name === 'mathInline' || node.type.name === 'mathBlock') count++;
+ });
+ return count;
+ });
+
+ expect(mathNodeCount).toBeGreaterThan(0);
+ });
+
+ test('renders MathML elements in the DOM', async ({ superdoc }) => {
+ await superdoc.loadDocument(ALL_OBJECTS_DOC);
+ await superdoc.waitForStable();
+
+ // Verify