Skip to content

Version Packages (next) - #3601

Open
ecoscript[bot] wants to merge 1 commit into
nextfrom
changeset-release/next
Open

ecoscript[bot] wants to merge 1 commit into
nextfrom
changeset-release/next

Conversation

@ecoscript

@ecoscript ecoscript Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

@portabletext/editor@9.0.0-next.0

Major Changes

  • #3619 b993cec Thanks @christianhg! - feat!: replace the patches event with apply

    editor.send({type: 'apply', patches}) applies patches that came from someone else, such as a collaborator's change arriving from your backend. It replaces the patches event, which is removed along with the PatchesEvent type.

    The patches apply in order as another writer's change. They don't become steps in your undo history, and the caret stays put in text they don't touch. If they change the content, the editor emits one remote change event, followed by a correction: true change if it had to fix something. An unset of the whole value clears the undo history.

    Every patch counts as another writer's, so the patches need no origin and there is no snapshot to pass. If you relayed your own editor's patches back to it marked origin: 'local', stop sending those, since apply would apply them a second time.

    // Before
    editor.send({type: 'patches', patches: incomingPatches, snapshot})
    
    // After
    editor.send({type: 'apply', patches: incomingPatches})

    The ApplyEvent type is exported.

Minor Changes

  • #3413 77b4cda Thanks @christianhg! - feat: emit change events for local and remote changes, with corrections marked

    The editor now emits a change event, synchronously, every time its content changes. Each event carries the operations the editor applied to its content (the public Operation type), never the input it received:

    editor.on('change', (event) => {
      if (event.origin === 'local') {
        // `event.patches` is the change as Sanity patches.
        save(event.patches)
      }
    })
    • origin: 'local': what a user action did, including everything the action's Behaviors raised. Undo and redo are one action each. Local changes arrive in application order, and one user action can produce several when the editor fixes something mid-action. patches holds the change as Sanity patches, the same patches the patch event emits for the action, available as soon as the action finishes instead of on the mutation event's debounced schedule.
    • origin: 'local' with correction: true: the editor's own fixes to the content, such as merging adjacent spans that share marks after a paste, or replacing a duplicate _key. A correction follows the change that caused it, with its own operations and patches. Fixes to content the editor received through apply or update value are corrections too. Registering or unregistering a node with editor.registerNode after load can make the editor fix existing content, and those fixes are corrections. A host that saves every local change's patches saves corrections as well. A correction to received content arrives even when the editor drops it from mutation because a newer value or a conflicting remote patch arrived first. When the editor repairs a value it receives, by giving a child without a _key a new key, adding an empty span to a text block without children, or removing an unused mark definition, the repair is a correction too, and the remote change holds the value as received, without the repair. Every child without a _key gets its own key in that correction. Corrections are never held back: a correction to the initial value, or to a value received while the editor is read-only, carries patches even though the editor sends no patch or mutation events for it.
    • origin: 'remote': one event per apply event and per update value that changed the content. When an operation listener sends a Behavior event or registers a node while an update value is applied, that change is emitted in between, so the update value arrives as a remote event before it and another after it. Remote changes carry no patches.

    Every change of one action is delivered before any change that a listener sets off while receiving them. Loading the initial value emits no change, except a correction when the editor fixes the loaded value, and a change never has an empty operations array. The empty block the editor shows in an empty field is part of the snapshot and never reported, but when the editor puts one back later, because the last block was deleted or another writer unset the field, its insert is in the operations of the change that caused it. After a remote unset it arrives as a correction with no patches, since the field is already gone. A long initial value that the editor loads across several ticks counts as one load, so its fixes arrive as one correction. ChangeEvent is exported as a beta type. EditorEmittedEvent gains the change member, so exhaustive switches over event.type gain a case.

    An operation listener now sees a block the editor repairs on arrival as it was received, followed by each repair as its own insert. A received text block with three or more children without a _key now gets their key patches in index order. When the DOM selection leaves a read-only editor's content, the editor now deselects through the select event with at: null, so Behaviors listening to select see it.

Patch Changes

  • #3655 216fb52 Thanks @christianhg! - fix: render large block lists in chunks at every depth

    Typing in a large document no longer rebuilds an element for every block in the list being edited. At 20,000 root blocks, a keystroke improves from about 180ms to 81ms. Typing 20 keys in a 2,000-block callout improves from about 930ms to 660ms. Splitting a block with Enter at 8,000 root blocks improves from about 104ms to 51ms. Block lists at the editor root and inside registered containers render in chunks whose elements are reused unless their blocks or container scope changed. Blocks keep their DOM nodes when chunks split or merge, so the caret, selection and IME composition are unaffected and the rendered DOM is unchanged. A chunk an edit leaves with 50 blocks or fewer is merged into a neighbour when the two hold 200 blocks or fewer together.

  • #3608 91d91a4 Thanks @christianhg! - fix: resolve paths with one walk and drop operations that address nothing

    A path with two node segments and no field name between them, such as [{_key: 'b1'}, {_key: 's1'}] or [0, 0], now addresses nothing at any depth: at the root, inside a container, inside a container inside a container, and inside a block object's own Portable Text field. A Behavior event, a remote patch, a select or a range decoration at such a path changes nothing, emits no patch and no change, and logs nothing. The other patches in the same apply event still apply. A select at such a path keeps the current selection, and a range decoration with such an endpoint is never listed, rendered or mapped.

    An edit at a well-formed path whose key is not in the document also changes nothing and emits nothing. Previously a set, unset or insert at a missing key still emitted a patch, and an unset or insert at a missing span key logged Cannot apply an "unset" (node removal) operation at path ... because the node was not found. The event still reaches Behaviors, and a select at a missing key behaves as before.

    // Previously edited the first span's text, emitted a patch and a change
    editor.send({type: 'set', at: [{_key: 'b1'}, {_key: 's1'}, 'text'], value: 'baz'})
    // Previously logged console.error "... because the node was not found"
    editor.send({type: 'apply', patches: [{type: 'unset', path: [{_key: 'b1'}, 'children', {_key: 'gone'}], origin: 'remote'}]})
  • #3602 be03281 Thanks @pepahlavacek! - fix: keep the caret after pasted blocks inserted mid-block

  • #3616 e4e4d1b Thanks @christianhg! - fix: address the item counted from the end when its key is empty or shared with a sibling

    A remote patch at a negative index now changes the item counted from the end of the array, like Sanity, even when that item's key is empty or shared with a sibling. Before, such a patch could land on another item with the same key.

  • #3607 0bab687 Thanks @christianhg! - fix: resolve negative indexes in remote patch paths from the end of the array

    Remote set, unset and single-item insert patches with negative indexes now resolve from the end of the array at any depth, like Sanity. A negative index that falls before the start of the array changes nothing, except that an insert at -1 into an empty array still inserts.

  • #3634 b3125b6 Thanks @christianhg! - fix: remove a block or span when set gives it a non-object value

    A set of a block or span to a value that is not an object, such as null or a string, now removes it. Before, the block stayed on screen while the host received set null, so anything typed in it afterwards was lost.

@portabletext/plugin-character-pair-decorator@9.0.28-next.0

Patch Changes

@portabletext/plugin-decorations@1.0.15-next.0

Patch Changes

@portabletext/plugin-dnd@2.0.29-next.0

Patch Changes

@portabletext/plugin-emoji-picker@8.0.28-next.0

Patch Changes

@portabletext/plugin-input-rule@7.0.28-next.0

Patch Changes

@portabletext/plugin-list-index@2.0.27-next.0

Patch Changes

@portabletext/plugin-markdown-shortcuts@9.0.28-next.0

Patch Changes

@portabletext/plugin-one-line@8.0.27-next.0

Patch Changes

@portabletext/plugin-paste-link@5.0.27-next.0

Patch Changes

@portabletext/plugin-table@2.0.27-next.0

Patch Changes

@portabletext/plugin-typeahead-picker@7.0.28-next.0

Patch Changes

@portabletext/plugin-typography@9.0.28-next.0

Patch Changes

@portabletext/toolbar@9.0.28-next.0

Patch Changes


Note

High Risk
Major breaking API (patches → apply) plus new change event semantics affect collaboration, persistence, and any exhaustive event handling; incorrect migration can double-apply patches or miss saves.

Overview
This Changesets release PR bumps versions and changelogs only—no new implementation in the diff. It cuts @portabletext/editor@9.0.0-next.0 (prerelease on next) and republishes all editor plugins/toolbar packages that depend on it to matching *-next.0 versions.

@portabletext/editor 9.0.0-next.0 bundles prior work already on next: Breaking: remote collaboration input moves from editor.send({type: 'patches', ...}) to apply (no snapshot / per-patch origin; PatchesEvent removed). Feature: synchronous change events for local, remote, and correction flows, with local patches for saving. Fixes: chunked rendering for huge block lists, stricter path resolution (invalid/missing paths no-op without spurious patches), caret after mid-block paste, Sanity-aligned negative array indexes on remote patches, and removing blocks/spans when set receives non-object values.

Integrators upgrading to this prerelease must migrate off the patches event and may need to listen for change instead of relying only on patch/mutation timing.

Reviewed by Cursor Bugbot for commit 8806cdf. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
portable-text-editor-documentation Ready Ready Preview Nov 13, 2026 12:55pm UTC
portable-text-example-basic Ready Ready Preview Nov 13, 2026 12:55pm UTC
portable-text-playground Ready Ready Preview Nov 13, 2026 12:55pm UTC

Request Review

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Stats

✅ No significant changes.

All scenario measurements (34)

🗺️ @portabletext/block-tools / @portabletext/block-tools · @portabletext/block-tools / @portabletext/block-tools/rules · @portabletext/editor / @portabletext/editor · @portabletext/editor / @portabletext/editor/behaviors · @portabletext/editor / @portabletext/editor/plugins · @portabletext/editor / @portabletext/editor/selectors · @portabletext/editor / @portabletext/editor/traversal · @portabletext/editor / @portabletext/editor/utils · @portabletext/editor / defineSchema · @portabletext/editor / useEditor · @portabletext/editor / typical · @portabletext/html / @portabletext/html · @portabletext/html / @portabletext/html/rules · @portabletext/html / htmlToPortableText · @portabletext/keyboard-shortcuts / @portabletext/keyboard-shortcuts · @portabletext/patches / @portabletext/patches · @portabletext/repair / @portabletext/repair · @portabletext/sanity-bridge / @portabletext/sanity-bridge · @portabletext/schema / @portabletext/schema · @portabletext/plugin-character-pair-decorator / @portabletext/plugin-character-pair-decorator · @portabletext/plugin-decorations / @portabletext/plugin-decorations · @portabletext/plugin-dnd / @portabletext/plugin-dnd · @portabletext/plugin-emoji-picker / @portabletext/plugin-emoji-picker · @portabletext/plugin-input-rule / @portabletext/plugin-input-rule · @portabletext/plugin-list-index / @portabletext/plugin-list-index · @portabletext/plugin-markdown-shortcuts / @portabletext/plugin-markdown-shortcuts · @portabletext/plugin-one-line / @portabletext/plugin-one-line · @portabletext/plugin-paste-link / @portabletext/plugin-paste-link · @portabletext/plugin-typeahead-picker / @portabletext/plugin-typeahead-picker · @portabletext/plugin-typography / @portabletext/plugin-typography · @portabletext/toolbar / @portabletext/toolbar · @portabletext/toolbar / typical · @portabletext/plugin-table / @portabletext/plugin-table · @portabletext/plugin-table / @portabletext/plugin-table/ui · Artifacts

Scenario Kind Bundle (raw / gzip) Gzip change Import time Import change
⚪ @portabletext/block-tools / @portabletext/block-tools export 91.8 KB / 12.9 KB None 13 ms +0 ms, +1.7%
⚪ @portabletext/block-tools / @portabletext/block-tools/rules export 15.2 KB / 2.1 KB None 5 ms +0 ms, +3.6%
⚪ @portabletext/editor / @portabletext/editor export 1.05 MB / 133.7 KB None 66 ms +2 ms, +2.4%
⚪ @portabletext/editor / @portabletext/editor/behaviors export 4.3 KB / 147 B None 2 ms +0 ms, +2.9%
⚪ @portabletext/editor / @portabletext/editor/plugins export 5.1 KB / 645 B None 7 ms +0 ms, +3.7%
⚪ @portabletext/editor / @portabletext/editor/selectors export 101.1 KB / 10.1 KB None 11 ms +0 ms, +2.4%
⚪ @portabletext/editor / @portabletext/editor/traversal export 50.9 KB / 4.9 KB None 6 ms -0 ms, -1.6%
⚪ @portabletext/editor / @portabletext/editor/utils export 37.1 KB / 4.7 KB None 6 ms +0 ms, +1.3%
⚪ @portabletext/editor / defineSchema consumer 658 B / 69 B None None None
⚪ @portabletext/editor / typical consumer 1.04 MB / 133.5 KB None None None
⚪ @portabletext/editor / useEditor consumer 1.6 KB / 248 B None None None
⚪ @portabletext/html / @portabletext/html export 69.3 KB / 10.1 KB None 7 ms +0 ms, +2.4%
⚪ @portabletext/html / @portabletext/html/rules export 15.2 KB / 2.1 KB None 4 ms -0 ms, -0.2%
⚪ @portabletext/html / htmlToPortableText consumer 69.3 KB / 10.1 KB None None None
⚪ @portabletext/keyboard-shortcuts / @portabletext/keyboard-shortcuts export 7.0 KB / 858 B None 2 ms +0 ms, +4.4%
⚪ @portabletext/patches / @portabletext/patches export 50.0 KB / 7.7 KB None 4 ms +0 ms, +4.3%
⚪ @portabletext/plugin-character-pair-decorator / @portabletext/plugin-character-pair-decorator export 124.1 KB / 15.8 KB None None None
⚪ @portabletext/plugin-decorations / @portabletext/plugin-decorations export 16.4 KB / 2.3 KB None None None
⚪ @portabletext/plugin-dnd / @portabletext/plugin-dnd export 10.4 KB / 1.8 KB None None None
⚪ @portabletext/plugin-emoji-picker / @portabletext/plugin-emoji-picker export 150.1 KB / 18.6 KB None None None
⚪ @portabletext/plugin-input-rule / @portabletext/plugin-input-rule export 124.2 KB / 15.8 KB None None None
⚪ @portabletext/plugin-list-index / @portabletext/plugin-list-index export 7.5 KB / 1.2 KB None None None
⚪ @portabletext/plugin-markdown-shortcuts / @portabletext/plugin-markdown-shortcuts export 136.4 KB / 17.6 KB None None None
⚪ @portabletext/plugin-one-line / @portabletext/plugin-one-line export 2.6 KB / 608 B None None None
⚪ @portabletext/plugin-paste-link / @portabletext/plugin-paste-link export 4.3 KB / 1.0 KB None None None
⚪ @portabletext/plugin-table / @portabletext/plugin-table export 61.1 KB / 7.1 KB None None None
⚪ @portabletext/plugin-table / @portabletext/plugin-table/ui export 114.6 KB / 19.5 KB None None None
⚪ @portabletext/plugin-typeahead-picker / @portabletext/plugin-typeahead-picker export 166.2 KB / 19.9 KB None None None
⚪ @portabletext/plugin-typography / @portabletext/plugin-typography export 131.1 KB / 17.0 KB None None None
⚪ @portabletext/repair / @portabletext/repair export 4.5 KB / 636 B None 3 ms +0 ms, +3.0%
⚪ @portabletext/sanity-bridge / @portabletext/sanity-bridge export 27.0 KB / 3.0 KB None 4 ms -0 ms, -0.0%
⚪ @portabletext/schema / @portabletext/schema export 9.9 KB / 1.2 KB None 2 ms +0 ms, +7.3%
⚪ @portabletext/toolbar / @portabletext/toolbar export 153.3 KB / 17.3 KB None None None
⚪ @portabletext/toolbar / typical consumer 145.6 KB / 16.9 KB None None None

Significant means at least 1.0 KB and 1% gzip, or at least 5 ms and 10% import time.

@christianhg christianhg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not to be merged

@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from 8e11fc3 to aeaa7aa Compare November 6, 2026 18:09
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from aeaa7aa to f349a41 Compare November 9, 2026 07:26
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from f349a41 to 5764775 Compare November 10, 2026 09:35
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from 5764775 to ad2e251 Compare November 10, 2026 15:00
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from ad2e251 to c0ce8e3 Compare November 10, 2026 22:12
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from 5be0027 to c42d128 Compare November 12, 2026 09:49
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from c42d128 to a92235e Compare November 12, 2026 10:26
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from a92235e to 8e1d98b Compare November 12, 2026 15:04
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from 8e1d98b to bde2719 Compare November 12, 2026 17:33
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from bde2719 to 2b1ce11 Compare November 12, 2026 21:18
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from 2b1ce11 to 7ce3ee7 Compare November 13, 2026 06:08
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from 7ce3ee7 to c198946 Compare November 13, 2026 07:08
@ecoscript
ecoscript Bot force-pushed the changeset-release/next branch from c198946 to 6631aa6 Compare November 13, 2026 08:05

This branch was successfully deployed

3 active deployments
Preview – portable-text-editor-documentation — 8806cdf0 Deployed Nov 13, 2026 by vercel[bot]
Preview – portable-text-playground — 8806cdf0 Deployed Nov 13, 2026 by vercel[bot]
Preview – portable-text-example-basic — 8806cdf0 Deployed Nov 13, 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