Skip to content

fix: scope editor.dom.getSelectionRect to the editor#2884

Merged
christianhg merged 1 commit into
mainfrom
feat/editor-dom-get-caret-rect
Jul 1, 2026
Merged

fix: scope editor.dom.getSelectionRect to the editor#2884
christianhg merged 1 commit into
mainfrom
feat/editor-dom-get-caret-rect

Conversation

@christianhg

@christianhg christianhg commented Jun 30, 2026

Copy link
Copy Markdown
Member

editor.dom.getSelectionRect(snapshot) returned a rect for the wrong element whenever the editor didn't own the document selection. It read window.getSelection().getRangeAt(0), which is document-global: with focus outside the editor, or a second Portable Text editor mounted on the page, the browser's active selection is somewhere else entirely and getSelectionRect handed back that element's rect. The snapshot argument was dead weight, only null-checked, then ignored in favour of the live DOM.

It now maps the snapshot's own selection to a rect: resolveSelection resolves the keyed selection against the snapshot's value and schema, and DOMEditor.toDOMRange(...).getBoundingClientRect() reads its geometry. That scopes the result to this editor and makes snapshot load-bearing. The signature is unchanged, and the range whose rect you want is expressed the way the sibling dom methods already take theirs, through the snapshot's selection (getBlockNodes/getChildNodes read snapshot.context.selection too). A caret rect is a snapshot with a collapsed selection.

getSelectionRect has no callers inside the editor, so the change is inert internally. The pinning is a browser test with two stacked blocks that asserts the lower block's caret rect sits below the upper one's, and that an absent or unresolvable selection returns null. The gherkin delete suite, which drives the same toDOMRange-to-rect path through findCurrentLineRange, stays green.

The one behaviour it stops reflecting is a transient divergence between the DOM selection and the model, mid-IME composition where the DOM leads. Nothing consumed that.

@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3b67f9e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@portabletext/editor Patch
@portabletext/plugin-character-pair-decorator Patch
@portabletext/plugin-dnd Patch
@portabletext/plugin-emoji-picker Patch
@portabletext/plugin-input-rule Patch
@portabletext/plugin-list-index Patch
@portabletext/plugin-markdown-shortcuts Patch
@portabletext/plugin-one-line Patch
@portabletext/plugin-paste-link Patch
@portabletext/plugin-sdk-value Patch
@portabletext/plugin-table Patch
@portabletext/plugin-typeahead-picker Patch
@portabletext/plugin-typography Patch
@portabletext/toolbar Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
portable-text-editor-documentation Ready Ready Preview, Comment Jul 1, 2026 9:22am
portable-text-example-basic Ready Ready Preview, Comment Jul 1, 2026 9:22am
portable-text-playground Ready Ready Preview, Comment Jul 1, 2026 9:22am

Request Review

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @portabletext/editor

Compared against main (d45a254e)

@portabletext/editor

Metric Value vs main (d45a254)
Internal (raw) 792.8 KB +117 B, +0.0%
Internal (gzip) 151.5 KB +24 B, +0.0%
Bundled (raw) 1.40 MB +117 B, +0.0%
Bundled (gzip) 315.6 KB +23 B, +0.0%
Import time 96ms +1ms, +1.2%

@portabletext/editor/behaviors

Metric Value vs main (d45a254)
Internal (raw) 467 B -
Internal (gzip) 207 B -
Bundled (raw) 424 B -
Bundled (gzip) 171 B -
Import time 2ms +0ms, +1.2%

@portabletext/editor/plugins

Metric Value vs main (d45a254)
Internal (raw) 2.7 KB -
Internal (gzip) 894 B -
Bundled (raw) 2.5 KB -
Bundled (gzip) 827 B -
Import time 7ms -0ms, -2.1%

@portabletext/editor/selectors

Metric Value vs main (d45a254)
Internal (raw) 79.7 KB -
Internal (gzip) 14.6 KB -
Bundled (raw) 75.2 KB -
Bundled (gzip) 13.5 KB -
Import time 8ms +0ms, +2.0%

@portabletext/editor/traversal

Metric Value vs main (d45a254)
Internal (raw) 26.2 KB -
Internal (gzip) 5.2 KB -
Bundled (raw) 26.1 KB -
Bundled (gzip) 5.1 KB -
Import time 6ms +0ms, +1.5%

@portabletext/editor/utils

Metric Value vs main (d45a254)
Internal (raw) 29.7 KB -
Internal (gzip) 6.2 KB -
Bundled (raw) 27.3 KB -
Bundled (gzip) 5.8 KB -
Import time 6ms -0ms, -1.0%

🗺️ . · ./behaviors · ./plugins · ./selectors · ./traversal · ./utils · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @portabletext/markdown

Compared against main (d45a254e)

Metric Value vs main (d45a254)
Internal (raw) 53.0 KB -
Internal (gzip) 9.6 KB -
Bundled (raw) 348.2 KB -
Bundled (gzip) 96.1 KB -
Import time 39ms -1ms, -2.0%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@christianhg christianhg force-pushed the feat/editor-dom-get-caret-rect branch from 1796d0b to 1eb1151 Compare June 30, 2026 09:59
@christianhg christianhg force-pushed the feat/editor-dom-get-caret-rect branch from 1eb1151 to 4095f6b Compare July 1, 2026 08:52
@christianhg christianhg changed the title feat: add getCaretRect to editor.dom feat: generalize editor.dom.getSelectionRect to any range Jul 1, 2026
@christianhg christianhg force-pushed the feat/editor-dom-get-caret-rect branch from 4095f6b to 4a62478 Compare July 1, 2026 09:01
@christianhg christianhg changed the title feat: generalize editor.dom.getSelectionRect to any range fix: scope editor.dom.getSelectionRect to the editor Jul 1, 2026
@christianhg christianhg force-pushed the feat/editor-dom-get-caret-rect branch from 4a62478 to ca80a0b Compare July 1, 2026 09:05
@christianhg christianhg force-pushed the feat/editor-dom-get-caret-rect branch from ca80a0b to 91c9ba7 Compare July 1, 2026 09:16
@christianhg christianhg marked this pull request as ready for review July 1, 2026 09:19
`getSelectionRect(snapshot)` returned the rect of the live document
selection via `window.getSelection().getRangeAt(0)`, which is
document-global: with focus outside the editor, or multiple editors
mounted, it handed back a rect for an unrelated element, and the
`snapshot` parameter was only null-checked, never used.

It now maps the snapshot's selection through the model
(`resolveSelection` + `rangeToDOMRect`, the same model-to-pixels step
the engine's `findCurrentLineRange` uses), scoping the rect to this
editor's selection and making the parameter load-bearing. A caret rect
is a snapshot with a collapsed selection; an arbitrary range is a
snapshot carrying it, no new parameter, consistent with sibling `dom`
methods (`getBlockNodes`/`getChildNodes`) that read the snapshot's
selection.

The one behavior it no longer reflects is a transient DOM-vs-model
divergence (mid-IME composition); no caller relied on it.
@christianhg christianhg force-pushed the feat/editor-dom-get-caret-rect branch from 91c9ba7 to 3b67f9e Compare July 1, 2026 09:20
@christianhg christianhg enabled auto-merge (rebase) July 1, 2026 09:21
@christianhg christianhg merged commit 0b82b78 into main Jul 1, 2026
17 checks passed
@christianhg christianhg deleted the feat/editor-dom-get-caret-rect branch July 1, 2026 09:27
@ecoscript ecoscript Bot mentioned this pull request Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant