TipTap → BlockNote editor migration (0312) - #496
Merged
Conversation
added 19 commits
July 13, 2026 14:11
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…(0312) New packages/editor/src/blocknote layer: XNetEditor bound to the node Y.Doc via the content-v4 fragment (Yjs collab unchanged), custom specs (mention/hashtag/wikilink/inlineMath inline, callout/embed/pageEmbed/ databaseEmbed/taskViewEmbed/mermaid/richLink blocks, aiGenerated style), suggestion controllers for / @ # [[, sender-side URL paste to embed or rich-link card (0295), lazy legacy TipTap-fragment import via a dependency-free Yjs walker, and Block-JSON walkers preserving the 0168/0169 mention/tag contracts and block-id-keyed page tasks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…p extensions (0312) BREAKING CHANGE: EditorContribution.extension/toolbar are gone; plugins now contribute blockSpecs/inlineContentSpecs/styleSpecs (opaque, keyed by spec name) plus behavior-only slashMenuItems. The 0205 schema-skew guard now flags any contributed spec name not statically bundled in the host editor schema (XNET_SCHEMA_SPEC_NAMES). @tiptap/core dependency dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…ragment (0312) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
… to content-v4 (0312) apps/web: Editor/PageView/TaskInlineEditor on BlockNote; write-through task metadata retired (hosted tasks lock assignee/due-date to the doc); mermaid plugin contributes behavior only; dead toolbar/wikilink shims deleted. Readers: query search extractor, hub search indexer, database rich-text cells, history, and blob-cid scan all read content-v4 first with legacy fallback and render mention/hashtag/wikilink/inlineMath atoms as text. packages/react gains useMergedEditorContributions with the 0205 skew guard over bundled spec names. Root vitest inlines @blocknote/@Mantine ESM for jsdom suites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…eads) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…nt-v4 fragments (0312) New ai-surface/page-fragment module: Yjs walkers converting the content-v4 BlockNote fragment to markdown (legacy content fallback) and a bounded md-to-fragment writer with unique blockContainer ids; adapter mode renamed tiptap-yjs to blocknote-yjs (breaking). linkify header now mirrors BlockNote's link policy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…ing notes append BlockNote blocks (0312) Electron comments read nodes directly (inline anchors retired); enhance-append writes blockGroup/blockContainer structure into content-v4 (round-trip-verified against BlockNote's own serializer) with legacy-content read fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…face on XNetEditor (0312) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Removes RichTextEditor, FloatingToolbar, EditorToolbar, SlashMenu, suggestion popups, EmojiMenu, LinkTargetMenu, EditorSurface, live preview, markdown structural editing, document-compat, extension tiers, all TipTap extensions and NodeViews, the vendored toolbar UI kit, the legacy Y.Text core editor, and every @tiptap/* dependency. usePageComments is now editor-decoupled (panel threads only). New XNetEditor stories replace the RichTextEditor stories; root barrel exports only pure document utilities. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…r (0312) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…k; inline @blocknote for electron vitest (0312) Keeps every precached file under the PWA workbox 6MB ceiling (main chunk 6.2MB -> 5.0MB + 1.1MB editor-vendor). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…prosemirror-transform (0312) The convergence test exposed a real crash: prosemirror-state resolved a stale duplicate prosemirror-transform@1.10.5 without Transaction changedRange (BlockNote 0.51 calls it at mount) — pinned via pnpm override to ^1.12.0. Editor vitest project inlines @blocknote/@tiptap for a single prosemirror instance. Validation checklist annotated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
added 2 commits
July 13, 2026 21:27
…-blocknote-editor-migration
…m, e2e harness on XNetEditor (0312) - prettier --write over the 19 files CI flagged - ci.yml editor-ux unit step points at the blocknote test files (old chrome tests were deleted with the TipTap removal) - storybook plugins shim exports the spec-based schema-safety guard - tests/e2e harness mounts XNetEditor (RichTextEditor is gone) — this was the sync-matrix web convergence timeout - drop stale @xnetjs/editor/extensions vitest alias Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Contributor
|
Preview removed for PR #496. |
Contributor
🖼️ UI changes in this PRComponentsInteractionsAuto-captured by CI · run. Informational — not a blocking check. |
…groups (0312) Three defects found by running the editor-ux/sync-matrix e2e suites against the real app: - useCreateBlockNote depended on per-render host callback identities, recreating the editor every parent render — focus dropped ~11ms after landing and typed input vanished. Host callbacks now flow through refs; creation deps are schema/fragment/awareness only. - @blocknote/mantine resolved @mantine/core 9, which renders React 19 style context elements — under React 18 they become legacy Consumers and crash with 'render is not a function'. Pinned @mantine/core+hooks 8 (both allowed by @blocknote/mantine's peer range). - Custom slash items appended after the defaults made their groups non-contiguous, duplicating React keys on group headers; items are regrouped before filtering. editor-ux specs now select with the pointer (BlockNote opens the formatting toolbar on pointer selection, not shift+arrow). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
…-blocknote-editor-migration
…-blocknote-editor-migration
crs48
added a commit
that referenced
this pull request
Jul 15, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) 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 main, this PR will be updated. # Releases ## @xnetjs/plugins@2.0.0 ### Major Changes - [#496](#496) [`6a5a15e`](6a5a15e) Thanks [@crs48](https://github.com/crs48)! - AI page-markdown surface re-targeted to the BlockNote editor (exploration 0312). - **Breaking**: the page-markdown apply adapter mode `'tiptap-yjs'` is renamed to `'blocknote-yjs'` in `AiPageMarkdownApplyAdapterResult['mode']` and `AiPageMarkdownApplyResult['mode']`. Adapters that returned `mode: 'tiptap-yjs'` must return `'blocknote-yjs'` (or `'yjs'`/`'custom'`). - New Yjs-fragment ↔ markdown conversion for BlockNote (`content-v4`) documents, dependency-light (walks the Yjs XML tree directly, no editor/DOM): - `xnetPageFragmentToMarkdown(doc)` reads the BlockNote fragment (paragraph/heading/lists/check items/code/quote/callout/table + inline `mention`/`hashtag`/`wikilink`/`inlineMath` atoms), falling back to the legacy TipTap `content` fragment when `content-v4` is empty (`blockNoteFragmentToMarkdown` / `legacyFragmentToMarkdown` are also exported). - `replaceXNetPageFragmentWithMarkdown(doc, markdown)` writes the AI markdown subset (paragraphs, headings, bullet/numbered/check lists with nesting, fenced code, quotes, callouts, wikilinks) as BlockNote PM XML — `blockGroup > blockContainer` (unique `id` per block) wrappers — in one Yjs transaction. - `createBlockNotePageMarkdownAdapter({ resolveDoc })` packages both as an `AiPageMarkdownApplyAdapter` (plus `readMarkdown`) for `xnet_apply_page_markdown`, replacing the TipTap-era document bridge. - `XNET_PAGE_FRAGMENT_FIELD` (`'content-v4'`) and `XNET_PAGE_LEGACY_FRAGMENT_FIELD` (`'content'`) constants. - `@xnetjs/plugins` now depends on `yjs`; the unused `@tiptap/core` devDependency is gone. - [#496](#496) [`2a7b80f`](2a7b80f) Thanks [@crs48](https://github.com/crs48)! - `EditorContribution` carries BlockNote specs instead of TipTap extensions (exploration 0312). - **Breaking**: `EditorContribution.extension` (TipTap `Extension`) and `EditorContribution.toolbar` (`ToolbarContribution`, removed entirely) are gone. Plugins now contribute `blockSpecs` / `inlineContentSpecs` / `styleSpecs` (opaque BlockNote spec objects keyed by spec name) plus behavior-only `slashMenuItems`. - **Breaking**: the editor schema-skew guard is spec-based — `isSchemaDefiningExtension` is replaced by `isSchemaDefiningContribution`, and `findEditorSchemaRisks` / `warnOnEditorSchemaRisks` take the host's statically bundled spec names and flag any contributed spec outside that set (0205 invariant: schema specs must be identical across all collaborators or Yjs silently drops content). - `SlashCommandContext.editor` is now a BlockNote editor instance. - The `@tiptap/core` dependency is removed. ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/abuse@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/data@2.0.0 ### Minor Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - [#523](#523) [`a91f278`](a91f278) Thanks [@crs48](https://github.com/crs48)! - Drafts P2/P3 (exploration 0329): Patchwork-style branching on the change log. - `@xnetjs/data`: `Draft` node schema (`DRAFT_SCHEMA_IRI`, entries map, no nesting); `NodeStore` draft overlay — `setCheckedOutDraft` swaps member reads to clone content under original ids, redirects member writes to clones with lazy copy-on-write, mirrors clone change events to original-id subscribers, and exposes `getRaw` for overlay-free reads; device-local draft privacy set (`markDraftPrivate`/`isDraftPrivate`). - `@xnetjs/history`: draft lifecycle (`createDraft`, `forkNodeIntoDraft` — signed snapshot-create + pinned fork point + Yjs blob fork with state vector, `discardDraft`, `listDrafts`, never-fork policy); merge (`threeWayPropertyMerge`, `mergeDraft` — one merger-signed squash batch with draft-born promotion via temp ids, relation remapping, deletion conflict cards, idempotent Yjs delta lane, provenance) and `refreshDraftFromMain` (floating drafts). - `@xnetjs/runtime`: `NodeStoreSyncProvider` gains a `shouldPublish` predicate; the personal node-sync room excludes draft-private nodes, and draft privacy is rehydrated before sync starts. - [#523](#523) [`dd956e5`](dd956e5) Thanks [@crs48](https://github.com/crs48)! - Drafts UI plumbing (exploration 0329 P2/P3). - `@xnetjs/react`: new `useDraft(hostId)` hook (hooks sub-barrel) binding the draft engine and the NodeStore checkout overlay — list/create open drafts for a host, `checkout` (content-swap reads + lazy copy-on-write via `onMissingMember` → `forkNodeIntoDraft`), `returnToMain`, `discard` (leaves the checkout first), `merge` (merger-signed squash; returns conflict cards), `refresh` (fold main into the draft; pauses on conflicts), `setReviewRequested`, and `computeReview` — per-property three-way review cards (base at fork vs main now vs draft now) plus Yjs document-differs indicators, computed without applying anything. Database hosts widen the member scope to their row nodes. Re-exports `DraftMergeConflict`, `MergeDraftResult`, `RefreshDraftResult` for consumers. - `@xnetjs/data`: the `Draft` schema gains an optional `reviewRequested` checkbox (default `false`) — the P4 request-surfacing flag the Inbox/Requests surface lists open drafts by. - [#499](#499) [`e4cb876`](e4cb876) Thanks [@crs48](https://github.com/crs48)! - Add the `DebugReport` schema (`DebugReportSchema`, `type DebugReport`) for first-party crash/debug-report triage nodes (exploration 0315). Reports carry code-level diagnostics only, group by fingerprint, and inherit access from their diagnostics Space via the standard space cascade. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - [#498](#498) [`e2e78cd`](e2e78cd) Thanks [@crs48](https://github.com/crs48)! - Fix "no such column: p.tiebreak_key" on databases created before schema v8. The `tiebreak_key` column repair now runs before the first `node_properties` read (`getNode`/`getNodes`/`listNodes`/`queryNodes`), not just before writes — a fresh session that opened a document page hit the missing column on its first hydrate query before any write could trigger the lazy guard. Also adds the missing v8 entry to `SCHEMA_MIGRATIONS` (`ALTER TABLE node_properties ADD COLUMN tiebreak_key TEXT`). - Updated dependencies [[`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/sqlite@2.0.0 - @xnetjs/storage@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/history@2.0.0 ### Minor Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - [#523](#523) [`a91f278`](a91f278) Thanks [@crs48](https://github.com/crs48)! - Drafts P2/P3 (exploration 0329): Patchwork-style branching on the change log. - `@xnetjs/data`: `Draft` node schema (`DRAFT_SCHEMA_IRI`, entries map, no nesting); `NodeStore` draft overlay — `setCheckedOutDraft` swaps member reads to clone content under original ids, redirects member writes to clones with lazy copy-on-write, mirrors clone change events to original-id subscribers, and exposes `getRaw` for overlay-free reads; device-local draft privacy set (`markDraftPrivate`/`isDraftPrivate`). - `@xnetjs/history`: draft lifecycle (`createDraft`, `forkNodeIntoDraft` — signed snapshot-create + pinned fork point + Yjs blob fork with state vector, `discardDraft`, `listDrafts`, never-fork policy); merge (`threeWayPropertyMerge`, `mergeDraft` — one merger-signed squash batch with draft-born promotion via temp ids, relation remapping, deletion conflict cards, idempotent Yjs delta lane, provenance) and `refreshDraftFromMain` (floating drafts). - `@xnetjs/runtime`: `NodeStoreSyncProvider` gains a `shouldPublish` predicate; the personal node-sync room excludes draft-private nodes, and draft privacy is rehydrated before sync starts. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/react@2.0.0 ### Minor Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - [#523](#523) [`dd956e5`](dd956e5) Thanks [@crs48](https://github.com/crs48)! - Drafts UI plumbing (exploration 0329 P2/P3). - `@xnetjs/react`: new `useDraft(hostId)` hook (hooks sub-barrel) binding the draft engine and the NodeStore checkout overlay — list/create open drafts for a host, `checkout` (content-swap reads + lazy copy-on-write via `onMissingMember` → `forkNodeIntoDraft`), `returnToMain`, `discard` (leaves the checkout first), `merge` (merger-signed squash; returns conflict cards), `refresh` (fold main into the draft; pauses on conflicts), `setReviewRequested`, and `computeReview` — per-property three-way review cards (base at fork vs main now vs draft now) plus Yjs document-differs indicators, computed without applying anything. Database hosts widen the member scope to their row nodes. Re-exports `DraftMergeConflict`, `MergeDraftResult`, `RefreshDraftResult` for consumers. - `@xnetjs/data`: the `Draft` schema gains an optional `reviewRequested` checkbox (default `false`) — the P4 request-surfacing flag the Inbox/Requests surface lists open drafts by. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/history@2.0.0 - @xnetjs/runtime@0.5.0 - @xnetjs/data-bridge@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/runtime@0.5.0 ### Minor Changes - [#523](#523) [`a91f278`](a91f278) Thanks [@crs48](https://github.com/crs48)! - Drafts P2/P3 (exploration 0329): Patchwork-style branching on the change log. - `@xnetjs/data`: `Draft` node schema (`DRAFT_SCHEMA_IRI`, entries map, no nesting); `NodeStore` draft overlay — `setCheckedOutDraft` swaps member reads to clone content under original ids, redirects member writes to clones with lazy copy-on-write, mirrors clone change events to original-id subscribers, and exposes `getRaw` for overlay-free reads; device-local draft privacy set (`markDraftPrivate`/`isDraftPrivate`). - `@xnetjs/history`: draft lifecycle (`createDraft`, `forkNodeIntoDraft` — signed snapshot-create + pinned fork point + Yjs blob fork with state vector, `discardDraft`, `listDrafts`, never-fork policy); merge (`threeWayPropertyMerge`, `mergeDraft` — one merger-signed squash batch with draft-born promotion via temp ids, relation remapping, deletion conflict cards, idempotent Yjs delta lane, provenance) and `refreshDraftFromMain` (floating drafts). - `@xnetjs/runtime`: `NodeStoreSyncProvider` gains a `shouldPublish` predicate; the personal node-sync room excludes draft-private nodes, and draft privacy is rehydrated before sync starts. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/history@2.0.0 - @xnetjs/data-bridge@2.0.0 - @xnetjs/storage@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/sqlite@2.0.0 ### Minor Changes - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - [#498](#498) [`e2e78cd`](e2e78cd) Thanks [@crs48](https://github.com/crs48)! - Fix "no such column: p.tiebreak_key" on databases created before schema v8. The `tiebreak_key` column repair now runs before the first `node_properties` read (`getNode`/`getNodes`/`listNodes`/`queryNodes`), not just before writes — a fresh session that opened a document page hit the missing column on its first hydrate query before any write could trigger the lazy guard. Also adds the missing v8 entry to `SCHEMA_MIGRATIONS` (`ALTER TABLE node_properties ADD COLUMN tiebreak_key TEXT`). ## @xnetjs/abuse@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/cli@0.1.7 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/runtime@0.5.0 - @xnetjs/sqlite@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/crypto@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/core@2.0.0 ## @xnetjs/data-bridge@2.0.0 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/sqlite@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/identity@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/storage@2.0.0 ### Patch Changes - Updated dependencies [[`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/sqlite@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/sync@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/core@2.0.0 ## xnet-cloud@0.0.18 ### Patch Changes - Updated dependencies []: - @xnetjs/telemetry@0.0.1 - @xnetjs/cloud@0.0.1 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## xnet-demos@0.1.2 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/react@2.0.0 - @xnetjs/devtools@0.1.0 - @xnetjs/identity@2.0.0 ## @xnetjs/brain@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/vectors@0.0.1 ## @xnetjs/comms@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/dashboard@0.0.19 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/react@2.0.0 - @xnetjs/social@0.0.19 ## @xnetjs/labs@0.0.19 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 ## @xnetjs/licenses@0.0.19 ### Patch Changes - Updated dependencies []: - @xnetjs/crypto@2.0.0 ## @xnetjs/maps@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 ## @xnetjs/meetings@0.0.12 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 ## @xnetjs/server@0.0.18 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/data-bridge@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/social@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/unreal@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 ## xnet-desktop@2.0.0 Desktop shell release riding the @xnetjs/core 2.0.0 train. Desktop-specific changes are not tracked here; see the core packages' changelogs for what shipped.
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.




Summary
Implements exploration 0312: replaces the entire TipTap-based rich-text editor with a BlockNote-based one (overturning 0297's rejection now that document back-compat is waived — prerelease, breaking OK).
Net: −33k lines. The Yjs/LWW persistence path is unchanged; documents move to a new
content-v4fragment (BlockNote schema) with a lazy, lossy legacy import from the oldcontentfragment.What changed
packages/editor/src/blocknote/layer:XNetEditor(BlockNoteView + Yjs collab bound to the node's Y.Doc), custom specs — mention/hashtag/wikilink/inlineMath inline content, callout/embed/pageEmbed/databaseEmbed/taskViewEmbed/mermaid/richLink blocks, aiGenerated style — suggestion controllers for/@#[[(multi-char trigger is native), sender-side URL paste → embed/rich-link card (0295), lazy legacy import, Block-JSON walkers preserving the 0168/0169 mention/tag contracts, block-id-keyed page tasks.EditorContributionre-typed (breaking, major): BlockNote specs + slash items instead of TipTap extensions; 0205 schema-skew guard now checks contributed spec names against the statically bundled set.content-v4fragments (modeblocknote-yjs, breaking); 1,000-page byte-stability roundtrip gate still green.content-v4first with legacy fallback and render the new inline atoms as text.@tiptap/*deps (~33k lines). Comments are editor-decoupled: node-backed panel threads stay (0276); in-document marks retired (ThreadStore deferred).prosemirror-transform@1.10.5(noTransaction.changedRange) would have crashed BlockNote at mount — pinned to ^1.12.0 via pnpm override, caught by the new two-peer convergence test.Consciously dropped
Obsidian-style live syntax preview and the markdown source mode (BlockNote is WYSIWYG-first; markdown stays as shortcuts/paste/export).
Validation
editor-vendorsplit)@tiptap/imports remainno such column: p.tiebreak_key(0305 fallout, reproduced on unmodified main) — filed separately🤖 Generated with Claude Code