Skip to content

fix: merge consecutive text operations across forward-only behavior undo step boundary - #2747

Merged
christianhg merged 1 commit into
mainfrom
fix/undo-merge-forward-only-behavior
Jun 5, 2026
Merged

christianhg merged 1 commit into
mainfrom
fix/undo-merge-forward-only-behavior

Conversation

@christianhg

Copy link
Copy Markdown
Member

When a forward-only behavior intercepts one keystroke and the next keystroke has no matching behavior, the two ops land in separate undo steps even though the user perceives them as continuous typing. The intercepted keystroke gets an undoStepId from the mode === 'send' && !isNativeBehaviorEvent branch in perform-event.ts; the next keystroke flows through the native input path with undoStepId undefined. The createUndoSteps merge heuristic has branches for both-undefined and both-defined-different, but no branch for the current-undefined / previous-defined mismatch — so the merge skips and each keystroke becomes its own step.

The fix adds the symmetric merge branch for that case. The reverse direction (current defined, previous undefined) intentionally stays unmerged: a behavior claiming an event with an undoStepId signals an intentional undo step boundary, like input rules replacing -> with →.

Picks up the work from #2334 — the underlying gap still existed after the renames in #2700 and the createUndoSteps reorder in #2739.

Tests

tests/event.history.undo.test.tsx — a forward-only behavior on insert.text with text === 'a', then userEvent.type 'a' followed by 'b'. With the merge gap, one history.undo only pops the 'b' step, leaving "a"; with the symmetric branch, one undo restores the empty initial value. Fails on main, passes here.

@vercel

vercel Bot commented Jun 5, 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 Jun 5, 2026 8:04am
portable-text-example-basic Ready Ready Preview, Comment Jun 5, 2026 8:04am
portable-text-playground Ready Ready Preview, Comment Jun 5, 2026 8:04am
racetrack Ready Ready Preview, Comment Jun 5, 2026 8:04am

Request Review

@changeset-bot

changeset-bot Bot commented Jun 5, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0a7de60

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

This PR includes changesets to release 12 packages
Name Type
@portabletext/editor Patch
@portabletext/plugin-character-pair-decorator Patch
@portabletext/plugin-emoji-picker Patch
@portabletext/plugin-input-rule 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

@github-actions

github-actions Bot commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @portabletext/editor

Compared against main (22f6aa01)

@portabletext/editor

Metric Value vs main (22f6aa0)
Internal (raw) 777.5 KB +360 B, +0.0%
Internal (gzip) 147.7 KB +8 B, +0.0%
Bundled (raw) 1.38 MB +360 B, +0.0%
Bundled (gzip) 309.6 KB +13 B, +0.0%
Import time 97ms +2ms, +1.6%

@portabletext/editor/behaviors

Metric Value vs main (22f6aa0)
Internal (raw) 467 B -
Internal (gzip) 207 B -
Bundled (raw) 424 B -
Bundled (gzip) 171 B -
Import time 2ms -0ms, -1.6%

@portabletext/editor/plugins

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

@portabletext/editor/selectors

Metric Value vs main (22f6aa0)
Internal (raw) 79.4 KB -
Internal (gzip) 14.5 KB -
Bundled (raw) 74.9 KB -
Bundled (gzip) 13.4 KB -
Import time 8ms -0ms, -0.1%

@portabletext/editor/traversal

Metric Value vs main (22f6aa0)
Internal (raw) 25.5 KB -
Internal (gzip) 5.0 KB -
Bundled (raw) 25.5 KB -
Bundled (gzip) 5.0 KB -
Import time 6ms +0ms, +3.3%

@portabletext/editor/utils

Metric Value vs main (22f6aa0)
Internal (raw) 28.8 KB -
Internal (gzip) 6.0 KB -
Bundled (raw) 26.7 KB -
Bundled (gzip) 5.7 KB -
Import time 6ms -0ms, -0.6%

🗺️ . · ./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 (22f6aa01)

Metric Value vs main (22f6aa0)
Internal (raw) 53.0 KB -
Internal (gzip) 9.6 KB -
Bundled (raw) 347.6 KB -
Bundled (gzip) 96.0 KB -
Import time 40ms +1ms, +1.5%

🗺️ 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.

This branch was successfully deployed

4 active deployments
Preview – portable-text-editor-documentation — 0a7de600 Deployed Jun 5, 2026 by vercel[bot]
Preview – portable-text-playground — 0a7de600 Deployed Jun 5, 2026 by vercel[bot]
Preview – portable-text-example-basic — 0a7de600 Deployed Jun 5, 2026 by vercel[bot]
Preview – racetrack — 0a7de600 Deployed Jun 5, 2026 by vercel[bot]
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