Skip to content

Rich text: synchronize the selection before events that consume it#80151

Merged
ellatrix merged 3 commits into
trunkfrom
add/rich-text-ensure-selection-sync
Jul 13, 2026
Merged

Rich text: synchronize the selection before events that consume it#80151
ellatrix merged 3 commits into
trunkfrom
add/rich-text-ensure-selection-sync

Conversation

@ellatrix

@ellatrix ellatrix commented Jul 12, 2026

Copy link
Copy Markdown
Member

What?

Update the rich text selection (record and store) right before any event that acts on it: keydown, beforeinput, copy, cut and paste.

Why?

The browser fires selectionchange asynchronously, so when one of these events arrives first, handlers act on the previous selection: the caret ends up in the wrong place, and selection-dependent e2e tests flake. Fast input can always win that race, however the selection was moved (click, arrow key, split).

Syncing at these events is enough: only a handful of events act on the selection, while there are countless ways to move it.

How?

The existing selectionchange handler is also subscribed on capture of the five events, so it runs before any other handler, with all its guards unchanged. A snapshot of the last processed selection keeps repeat calls down to a few property comparisons: real work only happens when the selection moved and its selectionchange event hasn't been processed yet, which is exactly the race being closed. The snapshot also verifies the record still holds the processed offsets, since a render can rewrite the record without the selection moving, and it is cleared when the focus handler resets the record.

Extracted from #79105, which uses the same mechanism for blocks whose caret moves between blocks without focus events.

Written with the help of Claude Code.

🤖 Generated with Claude Code

The native selectionchange event is asynchronous and coalesced, so the
record and the store selection can be one selection behind the DOM
when an event that acts on them arrives, regardless of how the
selection got there. Synchronize on capture of the events that consume
the record, the store selection, or a value rendered from them:
keydown, beforeinput, copy, cut and paste. A selection snapshot skips
the work when the selection has already been processed, so the common
path adds a few property comparisons per event.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: ellatrix <ellatrix@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions github-actions Bot added the [Package] Rich text /packages/rich-text label Jul 12, 2026
@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

Size Change: +157 B (0%)

Total Size: 7.7 MB

📦 View Changed
Filename Size Change
build/scripts/rich-text/index.min.js 14.7 kB +157 B (+1.08%)

compressed-size-action

ellatrix and others added 2 commits July 12, 2026 16:57
The selectionchange path benefits from the same fast path: the event
delivered after a capture synchronization, and coalesced duplicates,
now cost a few property comparisons instead of recreating the record.
The capture events subscribe handleSelectionChange directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The record's selection may be rewritten from (possibly stale) props on
render without the DOM selection moving. A snapshot matching the DOM
alone would then skip synchronization against a diverged record.
Comparing the offsets the processing produced against the record
re-processes in that case and keeps the fast path across benign
renders.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Flaky tests detected in 3aab38f.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/29215311386
📝 Reported issues:

@ellatrix ellatrix merged commit 26ac9cc into trunk Jul 13, 2026
42 checks passed
@ellatrix ellatrix deleted the add/rich-text-ensure-selection-sync branch July 13, 2026 01:39
@github-actions github-actions Bot added this to the Gutenberg 23.6 milestone Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Rich text /packages/rich-text

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant