Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89dddc2a95 | ||
|
|
f583d2a211 | ||
|
|
1b0f2e5463 | ||
|
|
4404d090e4 | ||
|
|
d4d4ae5f53 | ||
|
|
4ffd3709f8 | ||
|
|
0419b74315 | ||
|
|
c951f2b7ea | ||
|
|
4e2d3c8ac6 | ||
|
|
8dc1296a0b | ||
|
|
4329997350 | ||
|
|
2b7ea8a2af | ||
|
|
4f47355893 | ||
|
|
6074f80257 | ||
|
|
42bbb44bfc | ||
|
|
cc2d7320aa | ||
|
|
e804dd7573 |
23
README.md
23
README.md
@@ -121,6 +121,8 @@ Showcase](https://yjs-diagram.synergy.codes/).
|
||||
* [Eclipse Theia](https://github.com/eclipse-theia/theia) - A cloud & desktop
|
||||
IDE that runs in the browser.
|
||||
* [ScienHub](https://scienhub.com) - Collaborative LaTeX editor in the browser.
|
||||
* [Open Collaboration Tools](https://www.open-collab.tools/) - Collaborative
|
||||
editing for your IDE or custom editor
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -161,6 +163,7 @@ are implemented in separate modules.
|
||||
| React | | [react-yjs](https://github.com/nikgraf/react-yjs) | [demo](https://react-yjs-example.vercel.app/) |
|
||||
| React / Vue / Svelte / MobX | | [SyncedStore](https://syncedstore.org) | [demo](https://syncedstore.org/docs/react) |
|
||||
| [mobx-keystone](https://mobx-keystone.js.org/) | | [mobx-keystone-yjs](https://github.com/xaviergonz/mobx-keystone/tree/master/packages/mobx-keystone-yjs) | [demo](https://mobx-keystone.js.org/examples/yjs-binding) |
|
||||
| [PSPDFKit](https://www.nutrient.io/) | | [yjs-pspdfkit](https://github.com/hoangqwe159/yjs-pspdfkit) | [demo](https://github.com/hoangqwe159/yjs-pspdfkit) |
|
||||
|
||||
### Providers
|
||||
|
||||
@@ -191,7 +194,7 @@ are available. Communication over the signaling servers can be encrypted by
|
||||
providing a shared secret, keeping the connection information and the shared
|
||||
document private.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/liveblocks/liveblocks">@liveblocks/yjs</a></dt>
|
||||
<dt><a href="https://github.com/liveblocks/liveblocks">@liveblocks/yjs </a> 🌟</dt>
|
||||
<dd>
|
||||
<a href="https://liveblocks.io/document/yjs">Liveblocks Yjs</a> provides a fully
|
||||
hosted WebSocket infrastructure and persisted data store for Yjs
|
||||
@@ -199,15 +202,23 @@ documents. No configuration or maintenance is required. It also features
|
||||
Yjs webhook events, REST API to read and update Yjs documents, and a
|
||||
browser DevTools extension.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/drifting-in-space/y-sweet">y-sweet</a></dt>
|
||||
<dt><a href="https://github.com/drifting-in-space/y-sweet">y-sweet</a> ⭐</dt>
|
||||
<dd>
|
||||
A standalone yjs server with persistence to S3 or filesystem. They offer a
|
||||
<a href="https://y-sweet.cloud">cloud service</a> as well.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/ueberdosis/hocuspocus">Hocuspocus</a></dt>
|
||||
<dt><a href="https://github.com/ueberdosis/hocuspocus">Hocuspocus</a> ⭐</dt>
|
||||
<dd>
|
||||
A standalone extensible yjs server with sqlite persistence, webhooks, auth and more.
|
||||
</dd>
|
||||
<dt><a href="https://docs.superviz.com/collaboration/integrations/YJS/overview">@superviz/yjs</a></dt>
|
||||
<dd>
|
||||
SuperViz Yjs Provider comes with a secure, scalable real-time infrastructure
|
||||
for Yjs documents, fully compatible with a set of real-time
|
||||
collaboration components offered by SuperViz. This solution ensures
|
||||
synchronization, offline editing, and real-time updates, enabling
|
||||
multiple users to collaborate effectively within shared workspaces.
|
||||
</dd>
|
||||
<dt><a href="https://docs.partykit.io/reference/y-partykit-api/">PartyKit</a></dt>
|
||||
<dd>
|
||||
Cloud service for building multiplayer apps.
|
||||
@@ -272,11 +283,6 @@ network provider.
|
||||
<dd>
|
||||
Adds persistent storage to a server with MongoDB. Can be used with the
|
||||
y-websocket provider.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/toeverything/AFFiNE/tree/master/packages/y-indexeddb">
|
||||
@toeverything/y-indexeddb</a></dt>
|
||||
<dd>
|
||||
Like y-indexeddb, but with sub-documents support and fully TypeScript.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/podraven/y-fire">y-fire</a></dt>
|
||||
<dd>
|
||||
@@ -315,6 +321,7 @@ language bindings to other languages
|
||||
* [yswift](https://github.com/y-crdt/yswift) - Swift binding
|
||||
* [yffi](https://github.com/y-crdt/y-crdt/tree/main/yffi) - C-FFI
|
||||
* [ywasm](https://github.com/y-crdt/y-crdt/tree/main/ywasm) - WASM binding
|
||||
* [y_ex](https://github.com/satoren/y_ex) - Elixir bindings
|
||||
* [ycs](https://github.com/yjs/ycs) - .Net compatible C# implementation.
|
||||
|
||||
## Getting Started
|
||||
|
||||
143
funding.json
Normal file
143
funding.json
Normal file
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"version": "v1.0.0",
|
||||
"entity": {
|
||||
"type": "group",
|
||||
"role": "steward",
|
||||
"name": "Kevin Jahns",
|
||||
"email": "kevin.jahns@protonmail.com",
|
||||
"phone": "",
|
||||
"description": "OSS Developer",
|
||||
"webpageUrl": {
|
||||
"url": "https://github.com/yjs"
|
||||
}
|
||||
},
|
||||
"projects": [
|
||||
{
|
||||
"guid": "yjs",
|
||||
"name": "Yjs",
|
||||
"description": "A library for building collaborative applications. #p2p #local-first #CRDT Funding this project will also enable me to maintain the other Yjs-related technologies.",
|
||||
"webpageUrl": {
|
||||
"url": "https://github.com/yjs/yjs"
|
||||
},
|
||||
"repositoryUrl": {
|
||||
"url": "https://github.com/yjs/yjs"
|
||||
},
|
||||
"licenses": [
|
||||
"spdx:MIT"
|
||||
],
|
||||
"tags": [
|
||||
"collaboration",
|
||||
"p2p",
|
||||
"CRDT",
|
||||
"rich-text",
|
||||
"real-time"
|
||||
]
|
||||
},
|
||||
{
|
||||
"guid": "ystream",
|
||||
"name": "Y/Stream",
|
||||
"description": "A provider for syncing millions of docs efficiently with other peers. This will become the foundation for building real local-first apps with Yjs.",
|
||||
"webpageUrl": {
|
||||
"url": "https://github.com/yjs/ystream",
|
||||
"wellKnown": "https://github.com/yjs/ystream/blob/main/.well-known/funding-manifest-urls"
|
||||
},
|
||||
"repositoryUrl": {
|
||||
"url": "https://github.com/yjs/ystream",
|
||||
"wellKnown": "https://github.com/yjs/ystream/blob/main/.well-known/funding-manifest-urls"
|
||||
},
|
||||
"licenses": [
|
||||
"spdx:MIT",
|
||||
"spdx:GPL-3.0"
|
||||
],
|
||||
"tags": [
|
||||
"privacy",
|
||||
"collaboration",
|
||||
"p2p",
|
||||
"CRDT",
|
||||
"rich-text",
|
||||
"real-time",
|
||||
"web-development"
|
||||
]
|
||||
}
|
||||
],
|
||||
"funding": {
|
||||
"channels": [
|
||||
{
|
||||
"guid": "github-sponsors",
|
||||
"type": "payment-provider",
|
||||
"address": "",
|
||||
"description": "For funding of the Yjs project"
|
||||
},
|
||||
{
|
||||
"guid": "y-collective",
|
||||
"type": "payment-provider",
|
||||
"address": "https://opencollective.com/y-collective",
|
||||
"description": "For funding the Y-CRDT - the Rust implementation of Yjs and other listed projects."
|
||||
}
|
||||
],
|
||||
"plans": [
|
||||
{
|
||||
"guid": "supporter",
|
||||
"status": "active",
|
||||
"name": "Supporter",
|
||||
"description": "",
|
||||
"amount": 0,
|
||||
"currency": "USD",
|
||||
"frequency": "monthly",
|
||||
"channels": [
|
||||
"github-sponsors",
|
||||
"y-collective"
|
||||
]
|
||||
},
|
||||
{
|
||||
"guid": "ystream-funding",
|
||||
"status": "active",
|
||||
"name": "YStream Funding",
|
||||
"description": "Fund the next generation of local-first providers.",
|
||||
"amount": 30000,
|
||||
"currency": "USD",
|
||||
"frequency": "one-time",
|
||||
"channels": [
|
||||
"github-sponsors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"guid": "bronze-sponsor",
|
||||
"status": "active",
|
||||
"name": "Bronze Sponsor",
|
||||
"description": "This is the recommended plan for companies that use Yjs.",
|
||||
"amount": 500,
|
||||
"currency": "USD",
|
||||
"frequency": "monthly",
|
||||
"channels": [
|
||||
"github-sponsors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"guid": "silver-sponsor",
|
||||
"status": "active",
|
||||
"name": "Silver Sponsor",
|
||||
"description": "This is the recommended plan for large/successfull companies that use Yjs.",
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
"frequency": "monthly",
|
||||
"channels": [
|
||||
"github-sponsors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"guid": "gold-sponsor",
|
||||
"status": "active",
|
||||
"name": "Gold Sponsor",
|
||||
"description": "This is the recommended plan for successful companies that build their entire product around Yjs-related technologies.",
|
||||
"amount": 3000,
|
||||
"currency": "USD",
|
||||
"frequency": "monthly",
|
||||
"channels": [
|
||||
"github-sponsors"
|
||||
]
|
||||
}
|
||||
],
|
||||
"history": null
|
||||
}
|
||||
}
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.6.20",
|
||||
"version": "13.6.21",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "yjs",
|
||||
"version": "13.6.20",
|
||||
"version": "13.6.21",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.98"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.6.20",
|
||||
"version": "13.6.21",
|
||||
"description": "Shared Editing Library",
|
||||
"main": "./dist/yjs.cjs",
|
||||
"module": "./dist/yjs.mjs",
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ContentType,
|
||||
followRedone,
|
||||
getItem,
|
||||
ID, Doc, AbstractType // eslint-disable-line
|
||||
StructStore, ID, Doc, AbstractType, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding'
|
||||
@@ -256,6 +256,18 @@ export const readRelativePosition = decoder => {
|
||||
*/
|
||||
export const decodeRelativePosition = uint8Array => readRelativePosition(decoding.createDecoder(uint8Array))
|
||||
|
||||
/**
|
||||
* @param {StructStore} store
|
||||
* @param {ID} id
|
||||
*/
|
||||
const getItemWithOffset = (store, id) => {
|
||||
const item = getItem(store, id)
|
||||
const diff = id.clock - item.id.clock
|
||||
return {
|
||||
item, diff
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a relative position to an absolute position.
|
||||
*
|
||||
@@ -286,7 +298,7 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndo
|
||||
if (getState(store, rightID.client) <= rightID.clock) {
|
||||
return null
|
||||
}
|
||||
const res = followUndoneDeletions ? followRedone(store, rightID) : { item: getItem(store, rightID), diff: 0 }
|
||||
const res = followUndoneDeletions ? followRedone(store, rightID) : getItemWithOffset(store, rightID)
|
||||
const right = res.item
|
||||
if (!(right instanceof Item)) {
|
||||
return null
|
||||
|
||||
@@ -15,15 +15,28 @@ import * as relativePositions from './relativePositions.tests.js'
|
||||
import { runTests } from 'lib0/testing'
|
||||
import { isBrowser, isNode } from 'lib0/environment'
|
||||
import * as log from 'lib0/logging'
|
||||
import { environment } from 'lib0'
|
||||
|
||||
if (isBrowser) {
|
||||
log.createVConsole(document.body)
|
||||
}
|
||||
runTests({
|
||||
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
const tests = {
|
||||
doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions
|
||||
}).then(success => {
|
||||
}
|
||||
|
||||
const run = async () => {
|
||||
if (environment.isNode) {
|
||||
// tests.nodejs = await import('./node.tests.js')
|
||||
}
|
||||
|
||||
const success = await runTests(tests)
|
||||
/* istanbul ignore next */
|
||||
if (isNode) {
|
||||
process.exit(success ? 0 : 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
run()
|
||||
|
||||
@@ -85,6 +85,26 @@ export const testRelativePositionCase6 = tc => {
|
||||
checkRelativePositions(ytext)
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing https://github.com/yjs/yjs/issues/657
|
||||
*
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testRelativePositionCase7 = tc => {
|
||||
const docA = new Y.Doc()
|
||||
const textA = docA.getText('text')
|
||||
textA.insert(0, 'abcde')
|
||||
// Create a relative position at index 2 in 'textA'
|
||||
const relativePosition = Y.createRelativePositionFromTypeIndex(textA, 2)
|
||||
// Verify that the absolutes positions on 'docA' are the same
|
||||
const absolutePositionWithFollow =
|
||||
Y.createAbsolutePositionFromRelativePosition(relativePosition, docA, true)
|
||||
const absolutePositionWithoutFollow =
|
||||
Y.createAbsolutePositionFromRelativePosition(relativePosition, docA, false)
|
||||
t.assert(absolutePositionWithFollow?.index === 2)
|
||||
t.assert(absolutePositionWithoutFollow?.index === 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user