feat: headless Yjs collaboration extension (beta) + baseUrl cleanup#16
Merged
Conversation
baseUrl is deprecated in TS 6.0 and removed in 7.0. Since TS 5.0 the paths map resolves relative to the tsconfig file, so baseUrl is no longer required. Drop it from the editor, ui and web tsconfigs and keep the existing paths mappings. Verified: @lexkit/editor build (incl. dts) and web typecheck both pass.
Add a first-class, transport-agnostic collaboration extension built on
Lexical's own CollaborationPlugin + @lexical/yjs (CRDT, so offline edits
merge conflict-free on reconnect).
- Single transport seam: providerFactory(id, docMap) => Provider. LexKit
ships no transport dependency; plug in y-websocket / y-webrtc /
y-indexeddb / Hocuspocus / Liveblocks / etc.
- yjs + @lexical/yjs declared as optional peer deps (they already arrive
via @lexical/react, a required peer) so non-collab editors pull in
nothing extra.
- Headless presence API: commands.collab.{connect,disconnect,toggle,
setLocalUser,getUsers,onUsersChange} + isCollaborating state query +
useCollaborators() hook, all via the standard extension contract.
- Themeable remote cursors via theme.collab.* and an optional
cursorsContainerRef.
- Inert until configured with a providerFactory, so it is safe to leave
in the extensions array unconditionally.
Note: collaborative undo/redo is not wired by Lexical's CollaborationPlugin;
do not register historyExtension while collaborating (documented).
In-memory transport (no network) relays two real Y.Docs through the actual extension + Lexical binding to verify: - text typed in one editor syncs to another - bidirectional edits converge (CRDT, no lost data) - offline edits replay on reconnect and the docs converge identically (state-vector equality) - the presence API (getUsers/onUsersChange/setLocalUser) reflects awareness - isCollaborating tracks the connection Notes for the harness: each editor needs its own CollaborationContext (the default shares a module-level yjsDocMap), docs need unique clientIDs (lib0's RNG collides under jsdom), and remote updates are relayed async to avoid re-entering the binding.
Add a CollaborationExtension docs page with a live, server-free demo: two same-origin tabs sync peer-to-peer over y-webrtc's BroadcastChannel. The demo loads the transport client-side only (ssr:false) so y-webrtc never runs during prerender. Docs cover the providerFactory seam, y-websocket/y-webrtc/y-indexeddb recipes, the useCollaborators presence hook, cursor theming, offline/CRDT behaviour and the history/undo caveat. Adds y-webrtc + yjs to the web app and a sidebar nav entry.
Derive the npm dist-tag from the version: a hyphenated prerelease (e.g. 0.1.0-beta.0) publishes under 'beta' and is marked as a GitHub prerelease, so 'npm i @lexkit/editor' stays on stable 'latest' while 'npm i @lexkit/editor@beta' gets the collaboration build. A beta can be cut from this branch via 'gh workflow run "Release @lexkit/editor" --ref feat/collaboration'. Bump @lexkit/editor to 0.1.0-beta.0.
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.
Headless Yjs collaboration +
baseUrlcleanupAdds first-class, transport-agnostic real-time collaboration to LexKit, built on Lexical's own
CollaborationPlugin+@lexical/yjs(a CRDT — offline edits merge conflict-free on reconnect). Also drops the deprecated TSbaseUrl.Targeting a beta release first: this bumps
@lexkit/editorto0.1.0-beta.0, which the release workflow now publishes under the npmbetadist-tag — sonpm i @lexkit/editorstays on stable whilenpm i @lexkit/editor@betagets collaboration. Promote to stable0.1.0onmainonce validated.What's in it
CollaborationExtension— plugs in like every other extension. The only required option isproviderFactory: (id, docMap) => Provider, the single seam where you choose a transport. LexKit bundles no transport;yjs+@lexical/yjsare optional peer deps (they already arrive via@lexical/react), so editors without the extension pull in nothing extra.commands.collab.{connect,disconnect,toggle,setLocalUser,getUsers,onUsersChange}, anisCollaboratingstate query, and auseCollaborators()hook to build any presence UI.theme.collab.*classes + optionalcursorsContainerRef./docs/extensions/CollaborationExtensionwith a live y-webrtc demo (two same-origin tabs sync via BroadcastChannel, no server), plus recipes for y-websocket / y-webrtc / y-indexeddb, theming, offline behaviour and the history caveat.baseUrlremoved from the editor/ui/web tsconfigs (TS 5+ resolvespathswithout it); fixes the TS 6/7 deprecation.Tests (all green, 20/20)
Headless two-editor harness over an in-memory transport relaying two real
Y.Docs through the actual extension + Lexical binding:isCollaboratingtracks the connectionCompatibility
Verified the build + full test suite pass against Lexical 0.45 (latest) as well as the pinned 0.35; peer range stays
>=0.34.Notes / limitations
CollaborationPlugindoesn't wire a Yjs undo manager, so don't registerhistoryExtensionwhile collaborating (Yjs owns history). Documented.