feat: @y/prosemirror v2 + @y/y v14 integration#2739
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
776ca34 to
5fae30a
Compare
5fae30a to
d70df64
Compare
@blocknote/ariakit
@blocknote/code-block
@blocknote/core
@blocknote/mantine
@blocknote/react
@blocknote/server-util
@blocknote/shadcn
@blocknote/xl-ai
@blocknote/xl-docx-exporter
@blocknote/xl-email-exporter
@blocknote/xl-multi-column
@blocknote/xl-odt-exporter
@blocknote/xl-pdf-exporter
commit: |
00569fc to
38d3e13
Compare
38d3e13 to
a56bb0d
Compare
a9f7067 to
2ad90dc
Compare
a56bb0d to
6b9a6f6
Compare
e44394d to
17ab49f
Compare
6b9a6f6 to
d13fcac
Compare
Move formatChangeLabel out of the core AttributionExtension and into the React layer, where rendering the tooltip's text is a view concern. Core now emits the raw change context (parsed `format` keys) on AttributionTooltipState instead of a pre-baked label, mirroring how getAttributionMarkClassName receives structured info for categorization. The internal grouping key for nested marks becomes an i18n-free `attributionIdentity`. React composes the fully-localized tooltip text — "Inserted by: x", "Deleted by: x", "Formatting change (Bold, Italic) by: x" — via new function-valued `suggestion_changes` dictionary entries, translated across all locales, plus a configurable `formatChangeLabel` for the format list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sentinel
Rework the versioning contract for clarity and consistency:
- Rename endpoint + extension methods to a bare-verb scheme:
updateSnapshotName -> rename, deleteSnapshot -> remove; extension
methods/flags follow (canCreate, canRestore, canRename, canRemove,
canCompare, canPreviewCurrent).
- Rename the two "current" option callbacks to getCurrentDocument
(live handle, Input) and serializeCurrentContent (detached copy,
Output), and generics I/O/A -> Input/Output/Attributions.
- Collapse the current-version sentinel to a single exported
constant: CURRENT_VERSION_ID is now a `unique symbol` used as both
the store sentinel and the synthetic list-entry id (VersionSnapshot.id
widened to `string | typeof CURRENT_VERSION_ID`). Removes the former
CURRENT_VERSION_ENTRY_ID string twin. React derives a local string
key; YHub builds the current entry directly instead of via the
string-typed wire helper.
- Tighten JSDoc across the versioning files (inline-arg docs, less
prose, {@link} cross-references).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an opt-in DiffVersioningExtension (@blocknote/core/y) that renders a read-only diff between two Block[] states with y-attributed-insert/delete marks — the same visual result as the Yjs collaboration adapter, but driven from plain block arrays with no server. Changes are attributed to a static User A / User B via a serverless Y.Attributions store; colors and hover tooltips reuse the composed AttributionExtension. The core inMemoryVersioning preview controller discovers the extension by key (type-only import, so the base @blocknote/core bundle keeps zero runtime @y/* dependency) and delegates diff rendering to it, falling back to a static document swap when it isn't registered. supportsComparison / canCompare are lazy getters gated on the extension's presence, so comparison UI is only offered when a diff can actually be rendered — independent of extension registration order. Move the shared docDiffToDelta and getProseMirrorTrFromYFragment helpers into y/utils.ts and reuse them from the Yjs adapter and snapshot builder. Wire the extension into the 02-versioning example. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xample snapshotBuilder.ts and seed.ts were only used by the versioning-yjs14 example but shipped in @blocknote/core's output. Move them into the example's src/, drop their exports from the y extension/versioning barrels, and delete their tests (demo-only, no coverage needed). Add @y/prosemirror to the example deps since buildSnapshots uses it directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Show the version history sidebar by default (no click-to-open), match the wrapper/layout/editor-panel structure used by the collaboration versioning examples, and use the shared VersioningSidebar directly. Drop the Filter dropdown along with the now-unused SettingsSelect/VersionHistorySidebar components and the react-icons dependency. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…w context
A version diff has a single conceptual author — the version that introduced
the changes — not two collaborators. Replace the static User A / User B model
in DiffVersioningExtension with the previewed version's name: renderDiff takes
a version label and attributes the y-attributed-* marks to a synthetic id that
encodes it (`version:<label>`), so each version's tooltip resolves to its own
name (the encoded id also keeps the user-store cache from showing a stale name
when switching comparisons). Tooltips read "…by {versionName}".
Thread the version metadata through a new `context` argument on
PreviewController.enterPreview (the previewed + compared-against snapshots the
extension already has in hand) instead of smuggling a display label through the
getAttributions channel — which is meant for who/when authorship data. The
in-memory controller reads snapshot.name directly, so the synthetic
getAttributions and InMemoryDiffAttribution type are gone.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… into user/ Extract User/UserStore/createUserStore from user/index.ts into user/UserStore.ts, turning index.ts into a barrel. Move userColors.ts out of y/extensions/ (it has no Yjs coupling — it operates on the generic User/UserStore) into user/, alongside the identity types it extends. Repoint the two consumers (AttributionExtension, YAttributionMarks) at the new path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Version snapshots now carry raw author user-ids on a new `by` field instead of a pre-resolved `secondaryLabel` string, and resolving those ids to usernames becomes a view concern: - `VersioningExtension` accepts `resolveUsers` and exposes the normalized `userStore` on its instance (mirroring CommentsExtension); `CollaborationExtension` passes its shared store in. - The YHub endpoints just split the activity `by` field into ids — no more reaching into the attribution extension's user store. `create` stamps the optimistic snapshot with the cursor user's id. - New `useVersionUsers`/`useSnapshotLabel` React hooks (mirroring `useCommentUsers`) resolve `by` ids reactively, so author labels update as user info loads into the store — no re-list needed. - `UserStore` gains `setUser` and resolvers now receive the store, so a resolver can return partial info synchronously and fill in the rest later.
Convert y-prosemirror e2e inline snapshots to file-based snapshots to work around a vite-plus-test inline-snapshot writer bug that misplaces values across assertions when many snapshots in a file change at once, making the suite non-deterministic run-to-run. Also make the suggestion fixture deterministic: fixed Y.Doc clientIDs, serialize ydocXml with a stable Y.baseRenderer, sort marks/attrs, and strengthen the settle-gate in waitForSuggestion. Skip the known runaway-loop concurrent table test and the large-diff-delete-all versioning scenario.
- yhub.ts: add per-response index to history snapshot ids
(history-${to}-${index}) so duplicate activity `to` values no longer
produce duplicate React keys; default activityLimit=50 and
groupMaxGap=1000 and only send group/groupMaxDuration when defined so
the server no longer receives String(undefined) (parseInt->NaN disabled
grouping); guard getVersionNamesMap fragment access with optional chain
- YSync.ts: expose fragment on the ySync extension so
getExtension("ySync").fragment resolves
- yhub.test.ts: update history id assertion to new scheme and add ySync
stub for the __bn_version_names name-store tests
- versioning-yjs14 example + versioning sidebar/styles: seed/reconcile
cleanup and UI updates
…js14 example Read groupMaxGap/groupMaxDuration fresh from options in the YHub list() endpoint so a caller mutating the passed options object reconfigures grouping on the next refresh (default groupMaxGap lowered to 10000ms). Add a floating gear-toggled "Configuration" panel to the yjs14 versioning example with sliders + number inputs for both grouping knobs, re-fetching the history sidebar live against the same document. Space seeded edits 30s-1h apart and assign authors in contiguous runs so same-author streaks actually group, and open the sidebar already well-grouped for a short, readable history.
matthewlipski
left a comment
There was a problem hiding this comment.
R.e. React components everything looks pretty much as I'd expect, comments are mostly small/non-actionable things
The three AttributionTooltip slots were the only ones in the UI packages rendering a DOM node without forwarding a ref. Wrap them in forwardRef to match the convention used by every other slot. The shadcn copy was a verbatim clone of the mantine one, so also switch it to the package's own cn() utility. Note the ref does not reach the span yet: GenericPopover owns the positioned element, and the controller's attributionTooltip?: FC<...> prop cannot carry a ref. This aligns the slots with the convention; plumbing a ref end-to-end would mean changing AttributionTooltipProps and the public controller prop for a ref nothing currently consumes.
The versioning sidebar CSS was duplicated byte-for-byte across the ariakit, mantine and shadcn stylesheets. None of it was library-specific, and the selectors were unscoped (unlike every neighbouring rule), so the three packages shipped colliding copies of the same 200 lines. Move it once into react/src/editor/styles.css, alongside .bn-threads-sidebar, which all three packages already import. This also removes 200 of the 276 lines of shadcn's stylesheet, the bulk of the raw CSS in a package that styles with Tailwind. Render Snapshot through the shadcn Card component with Tailwind merged alongside the bn- classes, as Comments/Card does — the utilities only neutralize Card's own defaults where they would fight the shared CSS. Colors are left as-is: no theme var matches the palette (--bn-colors-disabled-text is far lighter than the author-label gray, and there is no accent var), and .bn-threads-sidebar already uses the same .dark-plus-hex pattern.
The controller was building the tooltip's display text from the extension state and passing a pre-built string down. Move that into AttributionTooltip, leaving the controller to subscribe to the extension and position the popover. AttributionTooltipProps now carries the raw change context (users, modificationType, format) plus the configurable formatChangeLabel, instead of a resolved text/formatLabel pair. A custom tooltip gets the same inputs the default one does, so it can phrase or categorize changes itself rather than parsing a composed sentence.
Summary
Adds support for the new
@y/prosemirrorv2 and@y/yv14 (Yjs 14) packages alongside the existing Yjs 13 (yjs,y-prosemirror) integration. This includes new collaboration bindings, versioning extensions, forked document support, and suggestion tracking built on the v14 APIs.Rationale
The
@y/yv14 ecosystem introduces significant improvements over Yjs 13, including better TypeScript support, a new protocol layer, and improved ProseMirror bindings. This PR adds first-class support for v14 while keeping full backward compatibility with Yjs 13.Changes
New
@blocknote/core/yentry point@y/prosemirrorv2 with TipTap/BlockNote@y/prosemirrorv2@y/yv14 APIsVersioning system (
@blocknote/coreextensions)yjspackageVersioningSidebar,Snapshot,CurrentSnapshotEditor improvements
y-attributed-deletemark) to avoid corrupting suggestionsgetBlockInfoFromPosnow handles nodes with marks (suggested changes)moveBlocks,replaceBlocks,splitBlock,nestBlock) to handle marked/attributed nodesPatches
@y/prosemirror@2.0.0-2— extensive patch for BlockNote compatibility@y/y@14.0.0-rc.16— minor patchlib0@1.0.0-rc.13— compatibility patchExamples
Tests
Impact
@y/packages are optional peer dependencies@blocknote/core/yexport for v14 users, parallel to existing@blocknote/core/yjsScreenshots/Video
Live examples:
Checklist