Skip to content

Commit 6de21f6

Browse files
bjohasclaude
andcommitted
fix(super-editor): resolve child element in Editor.getElementAtPos
`view.domAtPos(pos)` returns `{node, offset}` where `node` is an element parent and `offset` is the child index when the position sits between block children. The previous implementation returned the parent directly, which for the editor root collapses every position into "the entire document" — `scrollIntoView()` on that element always lands at the top of the editor regardless of which heading or paragraph was requested. Resolve to `parent.childNodes[offset]` (clamped) when the returned node is an element parent. The text-node branch is unchanged. This fixes web/flow-layout `getElementAtPos` for positions on block boundaries; the presentation-editor branch isn't touched. Verified with a probe that calls the body-editor `getElementAtPos` for known heading positions (2725, 3513, 4477, 13264) — before this fix all four returned the same ProseMirror root DIV; after, each returns the actual heading's wrapper element. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent a6a6654 commit 6de21f6

1 file changed

Lines changed: 23 additions & 4 deletions

File tree

  • packages/super-editor/src/editors/v1/core

packages/super-editor/src/editors/v1/core/Editor.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,11 +1718,30 @@ export class Editor extends EventEmitter<EditorEventMap> {
17181718
const clampedPos = Math.max(0, Math.min(pos, maxPos));
17191719

17201720
try {
1721-
const { node } = this.view.domAtPos(clampedPos);
1722-
if (node && node.nodeType === 1) {
1723-
return node as HTMLElement;
1721+
// ProseMirror's domAtPos returns either:
1722+
// - { node: <text>, offset: <chars into text> }, or
1723+
// - { node: <element>, offset: <child index> } when the position is
1724+
// between block children.
1725+
// The previous version returned the parent in the second case, which
1726+
// for the editor root means the entire document — scrolling that into
1727+
// view always lands at the top. Resolve to the actual child element
1728+
// when the returned node is an element parent.
1729+
const { node, offset } = this.view.domAtPos(clampedPos);
1730+
if (!node) return null;
1731+
1732+
if (node.nodeType === 1) {
1733+
const parent = node as Element;
1734+
if (parent.childNodes?.length) {
1735+
const idx = Math.min(Math.max(0, offset), parent.childNodes.length - 1);
1736+
const child = parent.childNodes[idx];
1737+
if (child) {
1738+
if (child.nodeType === 1) return child as HTMLElement;
1739+
if (child.nodeType === 3) return (child as Node).parentElement;
1740+
}
1741+
}
1742+
return parent as HTMLElement;
17241743
}
1725-
if (node && node.nodeType === 3) {
1744+
if (node.nodeType === 3) {
17261745
return node.parentElement;
17271746
}
17281747
return node?.parentElement ?? null;

0 commit comments

Comments
 (0)