Skip to content

feat: Portable Text-native node traversal#2398

Merged
christianhg merged 1 commit intomainfrom
feat-node-traversal
Mar 24, 2026
Merged

feat: Portable Text-native node traversal#2398
christianhg merged 1 commit intomainfrom
feat-node-traversal

Conversation

@christianhg
Copy link
Member

@christianhg christianhg commented Mar 19, 2026

Replaces the vendored Slate traversal system with schema-driven functions that resolve children on any node type, not just .children. The old system hardcoded .children as the only field containing child nodes. The new functions use resolveChildArrayField to look up schema-defined child fields for container types like tables, where children live in fields like rows, cells, or content. Container traversal is gated by editableTypes, a Set on the editor listing which object types have registered renderers. Currently empty, so containers traverse as leaf nodes today. A future PR populates it based on registered renderers, enabling container editing without changes to the traversal layer.

All traversal functions return {node, path} | undefined instead of throwing on invalid paths. The editor runs in a racy DOM environment where transient path mismatches during React re-renders are expected, not exceptional.

This PR makes node finding container-aware. Node manipulation (operations, patches, normalization) still accesses .children directly and assumes a flat block/span structure. That layer is next.

Some call sites grew when higher-level wrappers like above(), levels(), and nodes() were inlined. These wrappers bundled Location resolution (converting Range/Point/Path to a starting path), ancestor walking, and match filtering into single calls. With them gone, callers compose the primitives directly, which is more explicit but more verbose. getAncestorTextBlock and getAncestorObjectNode cover the most common patterns cleanly. The remaining cases, particularly in delete-text.ts and remove-nodes.ts, carry the Location resolution and mode-filtering logic that the wrappers used to hide.

Also removes isElementReadOnly, hasInlines, and elementReadOnly (dead code).

@changeset-bot
Copy link

changeset-bot bot commented Mar 19, 2026

🦋 Changeset detected

Latest commit: 79e7e88

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

This PR includes changesets to release 11 packages
Name Type
@portabletext/editor Minor
@portabletext/plugin-character-pair-decorator Major
@portabletext/plugin-emoji-picker Patch
@portabletext/plugin-input-rule Patch
@portabletext/plugin-markdown-shortcuts Major
@portabletext/plugin-one-line Major
@portabletext/plugin-paste-link Major
@portabletext/plugin-sdk-value Major
@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
Copy link

vercel bot commented Mar 19, 2026

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 Mar 24, 2026 1:23pm
portable-text-example-basic Ready Ready Preview, Comment Mar 24, 2026 1:23pm
portable-text-playground Ready Ready Preview, Comment Mar 24, 2026 1:23pm

Request Review

@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2026

📦 Bundle Stats — @portabletext/editor

Compared against main (055bdb16)

@portabletext/editor

Metric Value vs main (055bdb1)
Internal (raw) 759.2 KB +4.2 KB, +0.6%
Internal (gzip) 142.9 KB +937 B, +0.6%
Bundled (raw) 1.37 MB +4.2 KB, +0.3%
Bundled (gzip) 305.7 KB +877 B, +0.3%
Import time 97ms -3ms, -3.3%

@portabletext/editor/behaviors

Metric Value vs main (055bdb1)
Internal (raw) 467 B -
Internal (gzip) 207 B -
Bundled (raw) 424 B -
Bundled (gzip) 171 B -
Import time 6ms +0ms, +0.0%

@portabletext/editor/plugins

Metric Value vs main (055bdb1)
Internal (raw) 2.5 KB -
Internal (gzip) 910 B -
Bundled (raw) 2.3 KB -
Bundled (gzip) 839 B -
Import time 12ms -0ms, -0.1%

@portabletext/editor/selectors

Metric Value vs main (055bdb1)
Internal (raw) 60.2 KB -
Internal (gzip) 9.4 KB -
Bundled (raw) 56.7 KB -
Bundled (gzip) 8.6 KB -
Import time 10ms -0ms, -0.5%

@portabletext/editor/utils

Metric Value vs main (055bdb1)
Internal (raw) 24.2 KB -
Internal (gzip) 4.7 KB -
Bundled (raw) 22.2 KB -
Bundled (gzip) 4.4 KB -
Import time 9ms +0ms, +0.8%

🗺️ . · ./behaviors · ./plugins · ./selectors · ./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.

@christianhg christianhg force-pushed the feat-node-traversal branch from e1709db to 54eedf1 Compare March 19, 2026 16:31
@christianhg christianhg force-pushed the feat-node-traversal branch from 54eedf1 to cfdcf10 Compare March 19, 2026 17:07
@christianhg christianhg force-pushed the feat-simpler-dom-mapping branch 5 times, most recently from a16d425 to 285fe40 Compare March 20, 2026 08:16
@christianhg christianhg force-pushed the feat-node-traversal branch from cfdcf10 to d648f4d Compare March 20, 2026 08:58
@christianhg christianhg force-pushed the feat-node-traversal branch from 88a9829 to d8e304e Compare March 20, 2026 09:18
@christianhg christianhg force-pushed the feat-node-traversal branch 3 times, most recently from e1ed99b to 5ceaf3a Compare March 20, 2026 10:13
Replaces the vendored Slate traversal system with schema-driven functions that
resolve children on any node type, not just `.children`. The old system
hardcoded `.children` as the only child field, which blocks first-class nesting
where children will live in schema-defined fields like `rows`, `cells`, or
`content`.
@ecoscript ecoscript bot mentioned this pull request Mar 24, 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