@wordpress/core-data 7.38.1-next.v.0 → 7.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/build/actions.cjs +17 -6
- package/build/actions.cjs.map +2 -2
- package/build/awareness/base-awareness.cjs +62 -0
- package/build/awareness/base-awareness.cjs.map +7 -0
- package/build/awareness/post-editor-awareness.cjs +4 -18
- package/build/awareness/post-editor-awareness.cjs.map +3 -3
- package/build/entities.cjs +14 -2
- package/build/entities.cjs.map +2 -2
- package/build/resolvers.cjs +40 -0
- package/build/resolvers.cjs.map +2 -2
- package/build/types.cjs.map +1 -1
- package/build/utils/block-selection-history.cjs +101 -0
- package/build/utils/block-selection-history.cjs.map +7 -0
- package/build/utils/crdt-selection.cjs +139 -0
- package/build/utils/crdt-selection.cjs.map +7 -0
- package/build/utils/crdt-user-selections.cjs +2 -2
- package/build/utils/crdt-user-selections.cjs.map +2 -2
- package/build/utils/crdt-utils.cjs +29 -0
- package/build/utils/crdt-utils.cjs.map +3 -3
- package/build/utils/crdt.cjs +16 -4
- package/build/utils/crdt.cjs.map +2 -2
- package/build-module/actions.mjs +17 -6
- package/build-module/actions.mjs.map +2 -2
- package/build-module/awareness/base-awareness.mjs +35 -0
- package/build-module/awareness/base-awareness.mjs.map +7 -0
- package/build-module/awareness/post-editor-awareness.mjs +4 -18
- package/build-module/awareness/post-editor-awareness.mjs.map +2 -2
- package/build-module/entities.mjs +15 -2
- package/build-module/entities.mjs.map +2 -2
- package/build-module/resolvers.mjs +40 -0
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/utils/block-selection-history.mjs +75 -0
- package/build-module/utils/block-selection-history.mjs.map +7 -0
- package/build-module/utils/crdt-selection.mjs +115 -0
- package/build-module/utils/crdt-selection.mjs.map +7 -0
- package/build-module/utils/crdt-user-selections.mjs +2 -2
- package/build-module/utils/crdt-user-selections.mjs.map +2 -2
- package/build-module/utils/crdt-utils.mjs +28 -0
- package/build-module/utils/crdt-utils.mjs.map +2 -2
- package/build-module/utils/crdt.mjs +18 -3
- package/build-module/utils/crdt.mjs.map +2 -2
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/awareness/base-awareness.d.ts +19 -0
- package/build-types/awareness/base-awareness.d.ts.map +1 -0
- package/build-types/awareness/post-editor-awareness.d.ts +7 -8
- package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/entity-types/test/attachment.test.d.ts +10 -0
- package/build-types/entity-types/test/attachment.test.d.ts.map +1 -0
- package/build-types/index.d.ts.map +1 -1
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/types.d.ts +12 -2
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils/block-selection-history.d.ts +47 -0
- package/build-types/utils/block-selection-history.d.ts.map +1 -0
- package/build-types/utils/crdt-selection.d.ts +16 -0
- package/build-types/utils/crdt-selection.d.ts.map +1 -0
- package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
- package/build-types/utils/crdt-utils.d.ts +12 -0
- package/build-types/utils/crdt-utils.d.ts.map +1 -1
- package/build-types/utils/crdt.d.ts +8 -14
- package/build-types/utils/crdt.d.ts.map +1 -1
- package/build-types/utils/test/block-selection-history.test.d.ts +2 -0
- package/build-types/utils/test/block-selection-history.test.d.ts.map +1 -0
- package/build-types/utils/test/crdt-blocks.d.ts +2 -0
- package/build-types/utils/test/crdt-blocks.d.ts.map +1 -0
- package/build-types/utils/test/crdt.d.ts +2 -0
- package/build-types/utils/test/crdt.d.ts.map +1 -0
- package/package.json +21 -19
- package/src/actions.js +40 -7
- package/src/awareness/base-awareness.ts +50 -0
- package/src/awareness/post-editor-awareness.ts +4 -24
- package/src/entities.js +18 -2
- package/src/entity-types/test/attachment.test.ts +4 -4
- package/src/resolvers.js +51 -0
- package/src/test/actions.js +402 -0
- package/src/test/resolvers.js +4 -0
- package/src/types.ts +12 -3
- package/src/utils/block-selection-history.ts +176 -0
- package/src/utils/crdt-selection.ts +205 -0
- package/src/utils/crdt-user-selections.ts +7 -3
- package/src/utils/crdt-utils.ts +54 -0
- package/src/utils/crdt.ts +36 -3
- package/src/utils/test/block-selection-history.test.ts +764 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { dispatch, select } from '@wordpress/data';
|
|
5
|
+
// @ts-expect-error No exported types.
|
|
6
|
+
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
7
|
+
// @ts-expect-error No exported types.
|
|
8
|
+
import { isUnmodifiedBlock } from '@wordpress/blocks';
|
|
9
|
+
import { type CRDTDoc, Y } from '@wordpress/sync';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
createBlockSelectionHistory,
|
|
16
|
+
YSelectionType,
|
|
17
|
+
type BlockSelectionHistory,
|
|
18
|
+
type YFullSelection,
|
|
19
|
+
type YSelection,
|
|
20
|
+
} from './block-selection-history';
|
|
21
|
+
import { findBlockByClientIdInDoc } from './crdt-utils';
|
|
22
|
+
import type { WPBlockSelection, WPSelection } from '../types';
|
|
23
|
+
|
|
24
|
+
// WeakMap to store BlockSelectionHistory instances per Y.Doc
|
|
25
|
+
const selectionHistoryMap = new WeakMap< CRDTDoc, BlockSelectionHistory >();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get or create a BlockSelectionHistory instance for a given Y.Doc.
|
|
29
|
+
*
|
|
30
|
+
* @param ydoc The Y.Doc to get the selection history for
|
|
31
|
+
* @return The BlockSelectionHistory instance
|
|
32
|
+
*/
|
|
33
|
+
function getBlockSelectionHistory( ydoc: CRDTDoc ): BlockSelectionHistory {
|
|
34
|
+
let history = selectionHistoryMap.get( ydoc );
|
|
35
|
+
|
|
36
|
+
if ( ! history ) {
|
|
37
|
+
history = createBlockSelectionHistory( ydoc );
|
|
38
|
+
selectionHistoryMap.set( ydoc, history );
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return history;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getSelectionHistory( ydoc: CRDTDoc ): YFullSelection[] {
|
|
45
|
+
return getBlockSelectionHistory( ydoc ).getSelectionHistory();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function updateSelectionHistory(
|
|
49
|
+
ydoc: CRDTDoc,
|
|
50
|
+
wpSelection: WPSelection
|
|
51
|
+
): void {
|
|
52
|
+
return getBlockSelectionHistory( ydoc ).updateSelection( wpSelection );
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Convert a YSelection to a WPBlockSelection.
|
|
57
|
+
* @param ySelection The YSelection (relative) to convert
|
|
58
|
+
* @param ydoc The Y.Doc to convert the selection to a block selection for
|
|
59
|
+
* @return The converted WPBlockSelection, or null if the conversion fails
|
|
60
|
+
*/
|
|
61
|
+
function convertYSelectionToBlockSelection(
|
|
62
|
+
ySelection: YSelection,
|
|
63
|
+
ydoc: Y.Doc
|
|
64
|
+
): WPBlockSelection | null {
|
|
65
|
+
if ( ySelection.type === YSelectionType.RelativeSelection ) {
|
|
66
|
+
const { relativePosition, attributeKey, clientId } = ySelection;
|
|
67
|
+
|
|
68
|
+
const absolutePosition = Y.createAbsolutePositionFromRelativePosition(
|
|
69
|
+
relativePosition,
|
|
70
|
+
ydoc
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if ( absolutePosition ) {
|
|
74
|
+
return {
|
|
75
|
+
clientId,
|
|
76
|
+
attributeKey,
|
|
77
|
+
offset: absolutePosition.index,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
} else if ( ySelection.type === YSelectionType.BlockSelection ) {
|
|
81
|
+
return {
|
|
82
|
+
clientId: ySelection.clientId,
|
|
83
|
+
attributeKey: undefined,
|
|
84
|
+
offset: undefined,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Given a Y.Doc and a selection history, find the most recent selection
|
|
93
|
+
* that exists in the document. Skip any selections that are not in the document.
|
|
94
|
+
* @param ydoc The Y.Doc to find the selection in
|
|
95
|
+
* @param selectionHistory The selection history to check
|
|
96
|
+
* @return The most recent selection that exists in the document, or null if no selection exists.
|
|
97
|
+
*/
|
|
98
|
+
function findSelectionFromHistory(
|
|
99
|
+
ydoc: Y.Doc,
|
|
100
|
+
selectionHistory: YFullSelection[]
|
|
101
|
+
): WPSelection | null {
|
|
102
|
+
// Try each position until we find one that exists in the document
|
|
103
|
+
for ( const positionToTry of selectionHistory ) {
|
|
104
|
+
const { start, end } = positionToTry;
|
|
105
|
+
const startBlock = findBlockByClientIdInDoc( start.clientId, ydoc );
|
|
106
|
+
const endBlock = findBlockByClientIdInDoc( end.clientId, ydoc );
|
|
107
|
+
|
|
108
|
+
if ( ! startBlock || ! endBlock ) {
|
|
109
|
+
// This block no longer exists, skip it.
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const startBlockSelection = convertYSelectionToBlockSelection(
|
|
114
|
+
start,
|
|
115
|
+
ydoc
|
|
116
|
+
);
|
|
117
|
+
const endBlockSelection = convertYSelectionToBlockSelection(
|
|
118
|
+
end,
|
|
119
|
+
ydoc
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if ( startBlockSelection === null || endBlockSelection === null ) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
selectionStart: startBlockSelection,
|
|
128
|
+
selectionEnd: endBlockSelection,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Restore the selection to the most recent selection in history that is
|
|
137
|
+
* available in the document.
|
|
138
|
+
* @param selectionHistory The selection history to restore
|
|
139
|
+
* @param ydoc The Y.Doc where blocks are stored
|
|
140
|
+
*/
|
|
141
|
+
export function restoreSelection(
|
|
142
|
+
selectionHistory: YFullSelection[],
|
|
143
|
+
ydoc: Y.Doc
|
|
144
|
+
): void {
|
|
145
|
+
// Find the most recent selection in history that is available in
|
|
146
|
+
// the document.
|
|
147
|
+
const selectionToRestore = findSelectionFromHistory(
|
|
148
|
+
ydoc,
|
|
149
|
+
selectionHistory
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
if ( selectionToRestore === null ) {
|
|
153
|
+
// Case 1: No blocks in history are available for restoration.
|
|
154
|
+
// Do nothing.
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const { getBlock } = select( blockEditorStore );
|
|
159
|
+
const { resetSelection } = dispatch( blockEditorStore );
|
|
160
|
+
const { selectionStart, selectionEnd } = selectionToRestore;
|
|
161
|
+
const isSelectionInSameBlock =
|
|
162
|
+
selectionStart.clientId === selectionEnd.clientId;
|
|
163
|
+
|
|
164
|
+
if ( isSelectionInSameBlock ) {
|
|
165
|
+
// Case 2: After content is restored, the selection is available
|
|
166
|
+
// within the same block
|
|
167
|
+
|
|
168
|
+
const block = getBlock( selectionStart.clientId );
|
|
169
|
+
const isBlockEmpty = block && isUnmodifiedBlock( block );
|
|
170
|
+
const isBeginningOfEmptyBlock =
|
|
171
|
+
0 === selectionStart.offset &&
|
|
172
|
+
0 === selectionEnd.offset &&
|
|
173
|
+
isBlockEmpty;
|
|
174
|
+
|
|
175
|
+
if ( isBeginningOfEmptyBlock ) {
|
|
176
|
+
// Case 2a: When the content in a block has been removed after an
|
|
177
|
+
// undo, WordPress will set the selection to the block's client ID
|
|
178
|
+
// with an undefined startOffset and endOffset.
|
|
179
|
+
//
|
|
180
|
+
// To match the default behavior and tests, exclude the selection
|
|
181
|
+
// offset when resetting to position 0.
|
|
182
|
+
const selectionStartWithoutOffset = {
|
|
183
|
+
clientId: selectionStart.clientId,
|
|
184
|
+
};
|
|
185
|
+
const selectionEndWithoutOffset = {
|
|
186
|
+
clientId: selectionEnd.clientId,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
resetSelection(
|
|
190
|
+
selectionStartWithoutOffset,
|
|
191
|
+
selectionEndWithoutOffset,
|
|
192
|
+
0
|
|
193
|
+
);
|
|
194
|
+
} else {
|
|
195
|
+
// Case 2b: Otherwise, reset including the saved selection offset.
|
|
196
|
+
resetSelection( selectionStart, selectionEnd, 0 );
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
// Case 3: A multi-block selection was made. resetSelection() can only
|
|
200
|
+
// restore selections within the same block.
|
|
201
|
+
// When a multi-block selection is made, selectionEnd represents
|
|
202
|
+
// where the user's cursor ended.
|
|
203
|
+
resetSelection( selectionEnd, selectionEnd, 0 );
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -189,12 +189,16 @@ function getCursorPosition(
|
|
|
189
189
|
blocks: YBlocks
|
|
190
190
|
): CursorPosition | null {
|
|
191
191
|
const block = findBlockByClientId( selection.clientId, blocks );
|
|
192
|
-
if (
|
|
192
|
+
if (
|
|
193
|
+
! block ||
|
|
194
|
+
! selection.attributeKey ||
|
|
195
|
+
undefined === selection.offset
|
|
196
|
+
) {
|
|
193
197
|
return null;
|
|
194
198
|
}
|
|
195
199
|
|
|
196
|
-
const attributes = block.get( 'attributes' )
|
|
197
|
-
const currentYText = attributes
|
|
200
|
+
const attributes = block.get( 'attributes' );
|
|
201
|
+
const currentYText = attributes?.get( selection.attributeKey ) as Y.Text;
|
|
198
202
|
|
|
199
203
|
const relativePosition = Y.createRelativePositionFromTypeIndex(
|
|
200
204
|
currentYText,
|
package/src/utils/crdt-utils.ts
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { Y } from '@wordpress/sync';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import type { YBlock, YBlocks } from './crdt-blocks';
|
|
10
|
+
import type { YPostRecord } from './crdt';
|
|
11
|
+
import { CRDT_RECORD_MAP_KEY } from '../sync';
|
|
12
|
+
|
|
6
13
|
/**
|
|
7
14
|
* A YMapRecord represents the shape of the data stored in a Y.Map.
|
|
8
15
|
*/
|
|
@@ -75,3 +82,50 @@ export function isYMap< T extends YMapRecord >(
|
|
|
75
82
|
): value is YMapWrap< T > {
|
|
76
83
|
return value instanceof Y.Map;
|
|
77
84
|
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Given a block ID and a Y.Doc, find the block in the document.
|
|
88
|
+
*
|
|
89
|
+
* @param blockId The block ID to find
|
|
90
|
+
* @param ydoc The Y.Doc to find the block in
|
|
91
|
+
* @return The block, or null if the block is not found
|
|
92
|
+
*/
|
|
93
|
+
export function findBlockByClientIdInDoc(
|
|
94
|
+
blockId: string,
|
|
95
|
+
ydoc: Y.Doc
|
|
96
|
+
): YBlock | null {
|
|
97
|
+
const ymap = getRootMap< YPostRecord >( ydoc, CRDT_RECORD_MAP_KEY );
|
|
98
|
+
const blocks = ymap.get( 'blocks' );
|
|
99
|
+
|
|
100
|
+
if ( ! ( blocks instanceof Y.Array ) ) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return findBlockByClientIdInBlocks( blockId, blocks );
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function findBlockByClientIdInBlocks(
|
|
108
|
+
blockId: string,
|
|
109
|
+
blocks: YBlocks
|
|
110
|
+
): YBlock | null {
|
|
111
|
+
for ( const block of blocks ) {
|
|
112
|
+
if ( block.get( 'clientId' ) === blockId ) {
|
|
113
|
+
return block;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const innerBlocks = block.get( 'innerBlocks' );
|
|
117
|
+
|
|
118
|
+
if ( innerBlocks && innerBlocks.length > 0 ) {
|
|
119
|
+
const innerBlock = findBlockByClientIdInBlocks(
|
|
120
|
+
blockId,
|
|
121
|
+
innerBlocks
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if ( innerBlock ) {
|
|
125
|
+
return innerBlock;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return null;
|
|
131
|
+
}
|
package/src/utils/crdt.ts
CHANGED
|
@@ -8,11 +8,18 @@ import fastDeepEqual from 'fast-deep-equal/es6/index.js';
|
|
|
8
8
|
*/
|
|
9
9
|
// @ts-expect-error No exported types.
|
|
10
10
|
import { __unstableSerializeAndClean } from '@wordpress/blocks';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
type CRDTDoc,
|
|
13
|
+
type ObjectData,
|
|
14
|
+
type SyncConfig,
|
|
15
|
+
Y,
|
|
16
|
+
} from '@wordpress/sync';
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* Internal dependencies
|
|
15
20
|
*/
|
|
21
|
+
import { BaseAwareness } from '../awareness/base-awareness';
|
|
22
|
+
import { type BaseState } from '../awareness/types';
|
|
16
23
|
import {
|
|
17
24
|
mergeCrdtBlocks,
|
|
18
25
|
type Block,
|
|
@@ -27,6 +34,7 @@ import {
|
|
|
27
34
|
WORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE,
|
|
28
35
|
} from '../sync';
|
|
29
36
|
import type { WPSelection } from '../types';
|
|
37
|
+
import { updateSelectionHistory } from './crdt-selection';
|
|
30
38
|
import {
|
|
31
39
|
createYMap,
|
|
32
40
|
getRootMap,
|
|
@@ -47,6 +55,7 @@ export type PostChanges = Partial< Post > & {
|
|
|
47
55
|
export interface YPostRecord extends YMapRecord {
|
|
48
56
|
author: number;
|
|
49
57
|
blocks: YBlocks;
|
|
58
|
+
categories: number[];
|
|
50
59
|
comment_status: string;
|
|
51
60
|
date: string | null;
|
|
52
61
|
excerpt: string;
|
|
@@ -66,6 +75,7 @@ export interface YPostRecord extends YMapRecord {
|
|
|
66
75
|
const allowedPostProperties = new Set< string >( [
|
|
67
76
|
'author',
|
|
68
77
|
'blocks',
|
|
78
|
+
'categories',
|
|
69
79
|
'comment_status',
|
|
70
80
|
'date',
|
|
71
81
|
'excerpt',
|
|
@@ -94,7 +104,7 @@ const disallowedPostMetaKeys = new Set< string >( [
|
|
|
94
104
|
* @param {Partial< ObjectData >} changes
|
|
95
105
|
* @return {void}
|
|
96
106
|
*/
|
|
97
|
-
|
|
107
|
+
function defaultApplyChangesToCRDTDoc(
|
|
98
108
|
ydoc: CRDTDoc,
|
|
99
109
|
changes: ObjectData
|
|
100
110
|
): void {
|
|
@@ -240,9 +250,22 @@ export function applyPostChangesToCRDTDoc(
|
|
|
240
250
|
}
|
|
241
251
|
}
|
|
242
252
|
} );
|
|
253
|
+
|
|
254
|
+
// Process changes that we don't want to persist to the CRDT document.
|
|
255
|
+
if ( changes.selection ) {
|
|
256
|
+
const selection = changes.selection;
|
|
257
|
+
// Persist selection changes at the end of the current event loop.
|
|
258
|
+
// This allows undo meta to be saved with the current selection before
|
|
259
|
+
// it is overwritten by the new selection from Gutenberg.
|
|
260
|
+
// Without this, selection history will already contain the latest
|
|
261
|
+
// selection (after this change) when the undo stack is saved.
|
|
262
|
+
setTimeout( () => {
|
|
263
|
+
updateSelectionHistory( ydoc, selection );
|
|
264
|
+
}, 0 );
|
|
265
|
+
}
|
|
243
266
|
}
|
|
244
267
|
|
|
245
|
-
|
|
268
|
+
function defaultGetChangesFromCRDTDoc( crdtDoc: CRDTDoc ): ObjectData {
|
|
246
269
|
return getRootMap( crdtDoc, CRDT_RECORD_MAP_KEY ).toJSON();
|
|
247
270
|
}
|
|
248
271
|
|
|
@@ -382,6 +405,16 @@ export function getPostChangesFromCRDTDoc(
|
|
|
382
405
|
return changes;
|
|
383
406
|
}
|
|
384
407
|
|
|
408
|
+
/**
|
|
409
|
+
* This default sync config can be used for entities that are flat maps of
|
|
410
|
+
* primitive values and do not require custom logic to merge changes.
|
|
411
|
+
*/
|
|
412
|
+
export const defaultSyncConfig: SyncConfig< BaseState > = {
|
|
413
|
+
applyChangesToCRDTDoc: defaultApplyChangesToCRDTDoc,
|
|
414
|
+
createAwareness: ( ydoc: CRDTDoc ) => new BaseAwareness( ydoc ),
|
|
415
|
+
getChangesFromCRDTDoc: defaultGetChangesFromCRDTDoc,
|
|
416
|
+
};
|
|
417
|
+
|
|
385
418
|
/**
|
|
386
419
|
* Extract the raw string value from a property that may be a string or an object
|
|
387
420
|
* with a `raw` property (`RenderedText`).
|