Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6208b82872 | ||
|
|
12a9134b09 | ||
|
|
7395229086 | ||
|
|
8fb73edd97 | ||
|
|
f1ad5686c1 | ||
|
|
ed9236bdc7 | ||
|
|
5405fd2d7c | ||
|
|
12667f6b66 | ||
|
|
3d7ef7e28b | ||
|
|
56267e0a7d | ||
|
|
da71f6fa45 | ||
|
|
588788fbef | ||
|
|
fb9df6efe2 | ||
|
|
a69ecb0287 | ||
|
|
923fc6e06e | ||
|
|
0fdfd93e4b | ||
|
|
e0e5f8d2ea | ||
|
|
daf034cf75 | ||
|
|
2157ebb4d0 |
@@ -66,7 +66,7 @@ fields, respectively. These are used when peers concurrently insert at the same
|
|||||||
location in a document. Though quite rare in practice, Yjs needs to make sure
|
location in a document. Though quite rare in practice, Yjs needs to make sure
|
||||||
the list items always resolve to the same order on all peers. The actual logic
|
the list items always resolve to the same order on all peers. The actual logic
|
||||||
is relatively simple - its only a couple dozen lines of code and it lives in
|
is relatively simple - its only a couple dozen lines of code and it lives in
|
||||||
the `Item#integrate()` method. The YATA paper has much more detail on the this
|
the `Item#integrate()` method. The YATA paper has much more detail on this
|
||||||
algorithm.
|
algorithm.
|
||||||
|
|
||||||
### Item Storage
|
### Item Storage
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -34,6 +34,8 @@ on Yjs. [ A local-first, privacy-first, open source
|
||||||
|
knowledge base. 🏅
|
||||||
* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2:
|
* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2:
|
||||||
* [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and
|
* [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and
|
||||||
community. :star:
|
community. :star:
|
||||||
@@ -52,6 +54,8 @@ on Yjs. [ Presentation app.
|
* [Slidebeamer](https://slidebeamer.com/) Presentation app.
|
||||||
* [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys.
|
* [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys.
|
||||||
* [Skiff](https://skiff.org/) Private, decentralized workspace.
|
* [Skiff](https://skiff.org/) Private, decentralized workspace.
|
||||||
|
* [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for
|
||||||
|
sharing analyses, documentation, spreadsheets, and dashboards.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
@@ -136,6 +140,11 @@ Use Matrix as transport and storage of Yjs updates, so you can focus building
|
|||||||
your client app and Matrix can provide powerful features like Authentication,
|
your client app and Matrix can provide powerful features like Authentication,
|
||||||
Authorization, Federation, hosting (self-hosting or SaaS) and even End-to-End
|
Authorization, Federation, hosting (self-hosting or SaaS) and even End-to-End
|
||||||
Encryption (E2EE).
|
Encryption (E2EE).
|
||||||
|
</dd>
|
||||||
|
<dt><a href="https://github.com/MaxNoetzold/y-mongodb-provider">y-mongodb-provider</a></dt>
|
||||||
|
<dd>
|
||||||
|
Adds persistent storage to a server with MongoDB. Can be used with the
|
||||||
|
y-websocket provider.
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
@@ -629,6 +638,8 @@ type. Doesn't log types that have not been defined (using
|
|||||||
<dd>Define a shared Y.Array type. Is equivalent to <code>y.get(string, Y.Array)</code>.</dd>
|
<dd>Define a shared Y.Array type. Is equivalent to <code>y.get(string, Y.Array)</code>.</dd>
|
||||||
<b><code>getMap(string):Y.Map</code></b>
|
<b><code>getMap(string):Y.Map</code></b>
|
||||||
<dd>Define a shared Y.Map type. Is equivalent to <code>y.get(string, Y.Map)</code>.</dd>
|
<dd>Define a shared Y.Map type. Is equivalent to <code>y.get(string, Y.Map)</code>.</dd>
|
||||||
|
<b><code>getText(string):Y.Text</code></b>
|
||||||
|
<dd>Define a shared Y.Text type. Is equivalent to <code>y.get(string, Y.Text)</code>.</dd>
|
||||||
<b><code>getXmlFragment(string):Y.XmlFragment</code></b>
|
<b><code>getXmlFragment(string):Y.XmlFragment</code></b>
|
||||||
<dd>Define a shared Y.XmlFragment type. Is equivalent to <code>y.get(string, Y.XmlFragment)</code>.</dd>
|
<dd>Define a shared Y.XmlFragment type. Is equivalent to <code>y.get(string, Y.XmlFragment)</code>.</dd>
|
||||||
<b><code>on(string, function)</code></b>
|
<b><code>on(string, function)</code></b>
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.41",
|
"version": "13.5.42",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.41",
|
"version": "13.5.42",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.49"
|
"lib0": "^0.2.49"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.41",
|
"version": "13.5.42",
|
||||||
"description": "Shared Editing Library",
|
"description": "Shared Editing Library",
|
||||||
"main": "./dist/yjs.cjs",
|
"main": "./dist/yjs.cjs",
|
||||||
"module": "./dist/yjs.mjs",
|
"module": "./dist/yjs.mjs",
|
||||||
|
|||||||
@@ -167,16 +167,11 @@ export class YMap extends AbstractType {
|
|||||||
* @param {function(MapType,string,YMap<MapType>):void} f A function to execute on every element of this YArray.
|
* @param {function(MapType,string,YMap<MapType>):void} f A function to execute on every element of this YArray.
|
||||||
*/
|
*/
|
||||||
forEach (f) {
|
forEach (f) {
|
||||||
/**
|
|
||||||
* @type {Object<string,MapType>}
|
|
||||||
*/
|
|
||||||
const map = {}
|
|
||||||
this._map.forEach((item, key) => {
|
this._map.forEach((item, key) => {
|
||||||
if (!item.deleted) {
|
if (!item.deleted) {
|
||||||
f(item.content.getContent()[item.length - 1], key, this)
|
f(item.content.getContent()[item.length - 1], key, this)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return map
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1018,12 +1018,12 @@ export class YText extends AbstractType {
|
|||||||
case ContentString: {
|
case ContentString: {
|
||||||
const cur = currentAttributes.get('ychange')
|
const cur = currentAttributes.get('ychange')
|
||||||
if (snapshot !== undefined && !isVisible(n, snapshot)) {
|
if (snapshot !== undefined && !isVisible(n, snapshot)) {
|
||||||
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'removed') {
|
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'removed') {
|
||||||
packStr()
|
packStr()
|
||||||
currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' })
|
currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' })
|
||||||
}
|
}
|
||||||
} else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) {
|
} else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) {
|
||||||
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'added') {
|
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'added') {
|
||||||
packStr()
|
packStr()
|
||||||
currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' })
|
currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ const popStackItem = (undoManager, stack, eventType) => {
|
|||||||
* @property {number} [UndoManagerOptions.captureTimeout=500]
|
* @property {number} [UndoManagerOptions.captureTimeout=500]
|
||||||
* @property {function(Transaction):boolean} [UndoManagerOptions.captureTransaction] Do not capture changes of a Transaction if result false.
|
* @property {function(Transaction):boolean} [UndoManagerOptions.captureTransaction] Do not capture changes of a Transaction if result false.
|
||||||
* @property {function(Item):boolean} [UndoManagerOptions.deleteFilter=()=>true] Sometimes
|
* @property {function(Item):boolean} [UndoManagerOptions.deleteFilter=()=>true] Sometimes
|
||||||
* it is necessary to filter whan an Undo/Redo operation can delete. If this
|
* it is necessary to filter what an Undo/Redo operation can delete. If this
|
||||||
* filter returns false, the type/item won't be deleted even it is in the
|
* filter returns false, the type/item won't be deleted even it is in the
|
||||||
* undo/redo scope.
|
* undo/redo scope.
|
||||||
* @property {Set<any>} [UndoManagerOptions.trackedOrigins=new Set([null])]
|
* @property {Set<any>} [UndoManagerOptions.trackedOrigins=new Set([null])]
|
||||||
@@ -192,6 +192,7 @@ export class UndoManager extends Observable {
|
|||||||
this.doc = doc
|
this.doc = doc
|
||||||
this.lastChange = 0
|
this.lastChange = 0
|
||||||
this.ignoreRemoteMapChanges = ignoreRemoteMapChanges
|
this.ignoreRemoteMapChanges = ignoreRemoteMapChanges
|
||||||
|
this.captureTimeout = captureTimeout
|
||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
*/
|
*/
|
||||||
@@ -223,7 +224,7 @@ export class UndoManager extends Observable {
|
|||||||
})
|
})
|
||||||
const now = time.getUnixTime()
|
const now = time.getUnixTime()
|
||||||
let didAdd = false
|
let didAdd = false
|
||||||
if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) {
|
if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) {
|
||||||
// append change to last stack op
|
// append change to last stack op
|
||||||
const lastOp = stack[stack.length - 1]
|
const lastOp = stack[stack.length - 1]
|
||||||
lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet])
|
lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet])
|
||||||
|
|||||||
@@ -3,6 +3,19 @@ import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint
|
|||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
export const testInfiniteCaptureTimeout = tc => {
|
||||||
|
const { array0 } = init(tc, { users: 3 })
|
||||||
|
const undoManager = new Y.UndoManager(array0, { captureTimeout: Number.MAX_VALUE })
|
||||||
|
array0.push([1, 2, 3])
|
||||||
|
undoManager.stopCapturing()
|
||||||
|
array0.push([4, 5, 6])
|
||||||
|
undoManager.undo()
|
||||||
|
t.compare(array0.toArray(), [1, 2, 3])
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user