[draft] Raise diff boundary for strict schemas via a matchNodes predicate - #267
Open
YousefED wants to merge 3 commits into
Open
[draft] Raise diff boundary for strict schemas via a matchNodes predicate#267YousefED wants to merge 3 commits into
YousefED wants to merge 3 commits into
Conversation
Add a `matchNodes` option to `syncPlugin`, forwarded to `lib0/delta.diff`
as a node-pairing predicate. It lets an integrator declare when two
same-named nodes should be treated as *different* (a whole-node replace)
instead of diffed in place, so a suggestion-mode type change inside a
strict container (e.g. BlockNote's `blockContainer`, content
`blockContent blockGroup?`) becomes two sibling containers rather than
two block-contents in one container - which the content expression
rejects ("Invalid content for node blockContainer").
- `lib0/delta.diff` gains an optional `matchNodes(a, b)` argument
(defaults to name-equality, so existing behaviour is unchanged).
Vendored via patch-package pending an upstream lib0 change.
- `syncPlugin({ matchNodes })` + `defaultMatchNodes`; the predicate is
forwarded only to the PM->Y write diff. The "what identifies a node"
policy is schema-specific and therefore the integrator's to define -
the binding stays schema-agnostic.
- block-boundary.test.js: strict BlockNote-like schema; verifies the
boundary raise renders two sibling blockContainers (attribution as
node marks, no schema change) and that accept/reject converge. The
three flip tests crash without `matchNodes`; the seed test does not.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the storage-transform experiment and instead pass a BlockNote `matchNodes` policy (a blockContainer is identified by its first block-content child) to `syncPlugin`. A type-change suggestion now renders as two sibling blockContainers against the *unmodified* BlockNote schema - it already whitelists the `y-attributed-*` marks on blockContainer, so the attribution rides on node marks (no variant, no content relaxation, no storage migration). The demo keeps its own `node_modules/lib0`, so the lib0 patch is duplicated here via patch-package. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
✅ Deploy Preview for y-prosemirror-demo canceled.
|
The lib0 dependency is pinned to `^1.0.0-rc.14` (lockfiles at rc.14), but the vendored patches were still `lib0+1.0.0-rc.13.patch`, so a fresh `npm install` got rc.14 and patch-package failed to apply the `matchNodes` hook. rc.14's `diff` is structurally identical (it even now documents the name-based pairing the hook makes overridable), so the patch ports over unchanged. Bumps the demo's lib0 to rc.14 to match the root; replaces both patch files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
YousefED
force-pushed
the
schema-fix-matchnodes-diff-config
branch
from
June 11, 2026 14:43
370c8a9 to
66e4a86
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In suggestion mode, a node-type change (e.g. paragraph → heading) is diffed as a leaf-level
delete old + insert new. Inside a strict container the tombstoned old child ends up next to the inserted new child — two block-contents in one parent. For schemas like BlockNote'sblockContainer(contentblockContent blockGroup?) that's schema-invalid and throwsInvalid content for node blockContainer.Solution
This POC lets the diff decide modify-in-place vs. replace via a caller-supplied predicate, so the boundary can be raised to a whole node — producing two sibling
blockContainers (allowed byblockContainer+) instead of two block-contents in one.lib0/delta.diffgains an optionalmatchNodes(a, b)argument. The diff already pairs a removed node with an inserted node by name to decide "same node → descend / else → replace"; this just makes that one decision overridable. Defaults to name-equality, so existing behaviour is unchanged.syncPlugingains amatchNodesoption, forwarded to the PM→Y write diff. Declaring a node's identity (e.g. "ablockContaineris identified by its first block-content child") makes a child-type change report the two containers as different → whole-node replace → two siblings. A plain text edit keeps the same identity → descends and merges as before.The binding stays schema-agnostic — what identifies a node is the integrator's policy.
Caveats
<blockgroup>(nesting) related casesTests
tests/block-boundary.test.js— strict BlockNote-like schema; verifies the boundary raise renders two siblingblockContainer