@wordpress/core-data 7.39.0 → 7.39.1-next.v.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/build/awareness/base-awareness.cjs +4 -5
- package/build/awareness/base-awareness.cjs.map +2 -2
- package/build/awareness/post-editor-awareness.cjs +75 -2
- package/build/awareness/post-editor-awareness.cjs.map +2 -2
- package/build/awareness/types.cjs.map +1 -1
- package/build/hooks/use-post-editor-awareness-state.cjs +97 -0
- package/build/hooks/use-post-editor-awareness-state.cjs.map +7 -0
- package/build/types.cjs.map +1 -1
- package/build/utils/crdt-user-selections.cjs.map +2 -2
- package/build-module/awareness/base-awareness.mjs +5 -6
- package/build-module/awareness/base-awareness.mjs.map +2 -2
- package/build-module/awareness/post-editor-awareness.mjs +75 -2
- package/build-module/awareness/post-editor-awareness.mjs.map +2 -2
- package/build-module/hooks/use-post-editor-awareness-state.mjs +69 -0
- package/build-module/hooks/use-post-editor-awareness-state.mjs.map +7 -0
- package/build-module/utils/crdt-user-selections.mjs.map +2 -2
- package/build-types/awareness/base-awareness.d.ts +1 -1
- package/build-types/awareness/base-awareness.d.ts.map +1 -1
- package/build-types/awareness/post-editor-awareness.d.ts +23 -3
- package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
- package/build-types/awareness/types.d.ts +23 -1
- package/build-types/awareness/types.d.ts.map +1 -1
- package/build-types/hooks/use-post-editor-awareness-state.d.ts +35 -0
- package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -0
- package/build-types/types.d.ts +41 -0
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils/crdt-user-selections.d.ts +1 -34
- package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
- package/package.json +18 -18
- package/src/awareness/base-awareness.ts +5 -7
- package/src/awareness/post-editor-awareness.ts +108 -5
- package/src/awareness/types.ts +46 -1
- package/src/hooks/use-post-editor-awareness-state.ts +148 -0
- package/src/types.ts +71 -0
- package/src/utils/crdt-user-selections.ts +10 -62
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// packages/core-data/src/hooks/use-post-editor-awareness-state.ts
|
|
2
|
+
import { useEffect, useState } from "@wordpress/element";
|
|
3
|
+
import { getSyncManager } from "../sync.mjs";
|
|
4
|
+
var defaultState = {
|
|
5
|
+
activeUsers: [],
|
|
6
|
+
getAbsolutePositionIndex: () => null,
|
|
7
|
+
getDebugData: () => ({
|
|
8
|
+
doc: {},
|
|
9
|
+
clients: {},
|
|
10
|
+
userMap: {}
|
|
11
|
+
}),
|
|
12
|
+
isCurrentUserDisconnected: false
|
|
13
|
+
};
|
|
14
|
+
function getAwarenessState(awareness, newState) {
|
|
15
|
+
const activeUsers = newState ?? awareness.getCurrentState();
|
|
16
|
+
return {
|
|
17
|
+
activeUsers,
|
|
18
|
+
getAbsolutePositionIndex: (selection) => awareness.getAbsolutePositionIndex(selection),
|
|
19
|
+
getDebugData: () => awareness.getDebugData(),
|
|
20
|
+
isCurrentUserDisconnected: activeUsers.find((user) => user.isMe)?.isConnected === false
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function usePostEditorAwarenessState(postId, postType) {
|
|
24
|
+
const [state, setState] = useState(defaultState);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (null === postId || null === postType) {
|
|
27
|
+
setState(defaultState);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const objectType = `postType/${postType}`;
|
|
31
|
+
const objectId = postId.toString();
|
|
32
|
+
const awareness = getSyncManager()?.getAwareness(
|
|
33
|
+
objectType,
|
|
34
|
+
objectId
|
|
35
|
+
);
|
|
36
|
+
if (!awareness) {
|
|
37
|
+
setState(defaultState);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
awareness.setUp();
|
|
41
|
+
setState(getAwarenessState(awareness));
|
|
42
|
+
const unsubscribe = awareness?.onStateChange(
|
|
43
|
+
(newState) => {
|
|
44
|
+
setState(getAwarenessState(awareness, newState));
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
return unsubscribe;
|
|
48
|
+
}, [postId, postType]);
|
|
49
|
+
return state;
|
|
50
|
+
}
|
|
51
|
+
function useActiveUsers(postId, postType) {
|
|
52
|
+
return usePostEditorAwarenessState(postId, postType).activeUsers;
|
|
53
|
+
}
|
|
54
|
+
function useGetAbsolutePositionIndex(postId, postType) {
|
|
55
|
+
return usePostEditorAwarenessState(postId, postType).getAbsolutePositionIndex;
|
|
56
|
+
}
|
|
57
|
+
function useGetDebugData(postId, postType) {
|
|
58
|
+
return usePostEditorAwarenessState(postId, postType).getDebugData();
|
|
59
|
+
}
|
|
60
|
+
function useIsDisconnected(postId, postType) {
|
|
61
|
+
return usePostEditorAwarenessState(postId, postType).isCurrentUserDisconnected;
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
useActiveUsers,
|
|
65
|
+
useGetAbsolutePositionIndex,
|
|
66
|
+
useGetDebugData,
|
|
67
|
+
useIsDisconnected
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=use-post-editor-awareness-state.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/use-post-editor-awareness-state.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport { useEffect, useState } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport { getSyncManager } from '../sync';\nimport type {\n\tPostEditorAwarenessState as ActiveUser,\n\tYDocDebugData,\n} from '../awareness/types';\nimport type { SelectionCursor } from '../types';\nimport type { PostEditorAwareness } from '../awareness/post-editor-awareness';\n\ninterface AwarenessState {\n\tactiveUsers: ActiveUser[];\n\tgetAbsolutePositionIndex: ( selection: SelectionCursor ) => number | null;\n\tgetDebugData: () => YDocDebugData;\n\tisCurrentUserDisconnected: boolean;\n}\n\nconst defaultState: AwarenessState = {\n\tactiveUsers: [],\n\tgetAbsolutePositionIndex: () => null,\n\tgetDebugData: () => ( {\n\t\tdoc: {},\n\t\tclients: {},\n\t\tuserMap: {},\n\t} ),\n\tisCurrentUserDisconnected: false,\n};\n\nfunction getAwarenessState(\n\tawareness: PostEditorAwareness,\n\tnewState?: ActiveUser[]\n): AwarenessState {\n\tconst activeUsers = newState ?? awareness.getCurrentState();\n\n\treturn {\n\t\tactiveUsers,\n\t\tgetAbsolutePositionIndex: ( selection: SelectionCursor ) =>\n\t\t\tawareness.getAbsolutePositionIndex( selection ),\n\t\tgetDebugData: () => awareness.getDebugData(),\n\t\tisCurrentUserDisconnected:\n\t\t\tactiveUsers.find( ( user ) => user.isMe )?.isConnected === false,\n\t};\n}\n\nfunction usePostEditorAwarenessState(\n\tpostId: number | null,\n\tpostType: string | null\n): AwarenessState {\n\tconst [ state, setState ] = useState< AwarenessState >( defaultState );\n\n\tuseEffect( () => {\n\t\tif ( null === postId || null === postType ) {\n\t\t\tsetState( defaultState );\n\t\t\treturn;\n\t\t}\n\n\t\tconst objectType = `postType/${ postType }`;\n\t\tconst objectId = postId.toString();\n\t\tconst awareness = getSyncManager()?.getAwareness< PostEditorAwareness >(\n\t\t\tobjectType,\n\t\t\tobjectId\n\t\t);\n\n\t\tif ( ! awareness ) {\n\t\t\tsetState( defaultState );\n\t\t\treturn;\n\t\t}\n\n\t\tawareness.setUp();\n\n\t\t// Initialize with current awareness state.\n\t\tsetState( getAwarenessState( awareness ) );\n\n\t\tconst unsubscribe = awareness?.onStateChange(\n\t\t\t( newState: ActiveUser[] ) => {\n\t\t\t\tsetState( getAwarenessState( awareness, newState ) );\n\t\t\t}\n\t\t);\n\n\t\treturn unsubscribe;\n\t}, [ postId, postType ] );\n\n\treturn state;\n}\n\n/**\n * Hook to get the active users for a post editor.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return {ActiveUser[]} The active users.\n */\nexport function useActiveUsers(\n\tpostId: number | null,\n\tpostType: string | null\n): ActiveUser[] {\n\treturn usePostEditorAwarenessState( postId, postType ).activeUsers;\n}\n\n/**\n * Hook to get the absolute position index for a post editor.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return {SelectionCursor} The absolute position index.\n */\nexport function useGetAbsolutePositionIndex(\n\tpostId: number | null,\n\tpostType: string | null\n): ( selection: SelectionCursor ) => number | null {\n\treturn usePostEditorAwarenessState( postId, postType )\n\t\t.getAbsolutePositionIndex;\n}\n\n/**\n * Hook to get data for debugging, using the awareness state.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return {YDocDebugData} The debug data.\n */\nexport function useGetDebugData(\n\tpostId: number | null,\n\tpostType: string | null\n): YDocDebugData {\n\treturn usePostEditorAwarenessState( postId, postType ).getDebugData();\n}\n\n/**\n * Hook to check if the current user is disconnected.\n *\n * @param postId - The ID of the post.\n * @param postType - The type of the post.\n * @return {boolean} Whether the current user is disconnected.\n */\nexport function useIsDisconnected(\n\tpostId: number | null,\n\tpostType: string | null\n): boolean {\n\treturn usePostEditorAwarenessState( postId, postType )\n\t\t.isCurrentUserDisconnected;\n}\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAS,WAAW,gBAAgB;AAKpC,SAAS,sBAAsB;AAe/B,IAAM,eAA+B;AAAA,EACpC,aAAa,CAAC;AAAA,EACd,0BAA0B,MAAM;AAAA,EAChC,cAAc,OAAQ;AAAA,IACrB,KAAK,CAAC;AAAA,IACN,SAAS,CAAC;AAAA,IACV,SAAS,CAAC;AAAA,EACX;AAAA,EACA,2BAA2B;AAC5B;AAEA,SAAS,kBACR,WACA,UACiB;AACjB,QAAM,cAAc,YAAY,UAAU,gBAAgB;AAE1D,SAAO;AAAA,IACN;AAAA,IACA,0BAA0B,CAAE,cAC3B,UAAU,yBAA0B,SAAU;AAAA,IAC/C,cAAc,MAAM,UAAU,aAAa;AAAA,IAC3C,2BACC,YAAY,KAAM,CAAE,SAAU,KAAK,IAAK,GAAG,gBAAgB;AAAA,EAC7D;AACD;AAEA,SAAS,4BACR,QACA,UACiB;AACjB,QAAM,CAAE,OAAO,QAAS,IAAI,SAA4B,YAAa;AAErE,YAAW,MAAM;AAChB,QAAK,SAAS,UAAU,SAAS,UAAW;AAC3C,eAAU,YAAa;AACvB;AAAA,IACD;AAEA,UAAM,aAAa,YAAa,QAAS;AACzC,UAAM,WAAW,OAAO,SAAS;AACjC,UAAM,YAAY,eAAe,GAAG;AAAA,MACnC;AAAA,MACA;AAAA,IACD;AAEA,QAAK,CAAE,WAAY;AAClB,eAAU,YAAa;AACvB;AAAA,IACD;AAEA,cAAU,MAAM;AAGhB,aAAU,kBAAmB,SAAU,CAAE;AAEzC,UAAM,cAAc,WAAW;AAAA,MAC9B,CAAE,aAA4B;AAC7B,iBAAU,kBAAmB,WAAW,QAAS,CAAE;AAAA,MACpD;AAAA,IACD;AAEA,WAAO;AAAA,EACR,GAAG,CAAE,QAAQ,QAAS,CAAE;AAExB,SAAO;AACR;AASO,SAAS,eACf,QACA,UACe;AACf,SAAO,4BAA6B,QAAQ,QAAS,EAAE;AACxD;AASO,SAAS,4BACf,QACA,UACkD;AAClD,SAAO,4BAA6B,QAAQ,QAAS,EACnD;AACH;AASO,SAAS,gBACf,QACA,UACgB;AAChB,SAAO,4BAA6B,QAAQ,QAAS,EAAE,aAAa;AACrE;AASO,SAAS,kBACf,QACA,UACU;AACV,SAAO,4BAA6B,QAAQ,QAAS,EACnD;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/crdt-user-selections.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * External dependencies\n */\nimport { Y, CRDT_RECORD_MAP_KEY } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport type { YPostRecord } from './crdt';\nimport type { YBlock, YBlocks } from './crdt-blocks';\nimport { getRootMap } from './crdt-utils';\nimport type { WPBlockSelection } from '../types';\n\n/**\n * The type of selection.\n */\nexport enum SelectionType {\n\tNone = 'none',\n\tCursor = 'cursor',\n\tSelectionInOneBlock = 'selection-in-one-block',\n\tSelectionInMultipleBlocks = 'selection-in-multiple-blocks',\n\tWholeBlock = 'whole-block',\n}\n\n/**\n * The position of the cursor.\n */\nexport type CursorPosition = {\n\trelativePosition: Y.RelativePosition;\n\n\t// Also store the absolute offset index of the cursor from the perspective\n\t// of the user who is updating the selection.\n\t//\n\t// Do not use this value directly, instead use `createAbsolutePositionFromRelativePosition()`\n\t// on relativePosition for the most up-to-date positioning.\n\t//\n\t// This is used because local Y.Text changes (e.g. adding or deleting a character)\n\t// can result in the same relative position if it is pinned to an unchanged\n\t// character. With both of these values as editor state, a change in perceived\n\t// position will always result in a redraw.\n\tabsoluteOffset: number;\n};\n\nexport type SelectionNone = {\n\t// The user has not made a selection.\n\ttype: SelectionType.None;\n};\n\nexport type SelectionCursor = {\n\t// The user has a cursor position in a block with no text highlighted.\n\ttype: SelectionType.Cursor;\n\tblockId: string;\n\tcursorPosition: CursorPosition;\n};\n\nexport type SelectionInOneBlock = {\n\t// The user has highlighted text in a single block.\n\ttype: SelectionType.SelectionInOneBlock;\n\tblockId: string;\n\tcursorStartPosition: CursorPosition;\n\tcursorEndPosition: CursorPosition;\n};\n\nexport type SelectionInMultipleBlocks = {\n\t// The user has highlighted text over multiple blocks.\n\ttype: SelectionType.SelectionInMultipleBlocks;\n\tblockStartId: string;\n\tblockEndId: string;\n\tcursorStartPosition: CursorPosition;\n\tcursorEndPosition: CursorPosition;\n};\n\nexport type SelectionWholeBlock = {\n\t// The user has a non-text block selected, like an image block.\n\ttype: SelectionType.WholeBlock;\n\tblockId: string;\n};\n\nexport type SelectionState =\n\t| SelectionNone\n\t| SelectionCursor\n\t| SelectionInOneBlock\n\t| SelectionInMultipleBlocks\n\t| SelectionWholeBlock;\n\n/**\n * Converts WordPress block editor selection to a SelectionState.\n *\n * @param selectionStart - The start position of the selection\n * @param selectionEnd - The end position of the selection\n * @param yDoc - The Yjs document\n * @return The SelectionState\n */\nexport function getSelectionState(\n\tselectionStart: WPBlockSelection,\n\tselectionEnd: WPBlockSelection,\n\tyDoc: Y.Doc\n): SelectionState {\n\tconst ymap = getRootMap< YPostRecord >( yDoc, CRDT_RECORD_MAP_KEY );\n\tconst yBlocks = ymap.get( 'blocks' ) ?? new Y.Array< YBlock >();\n\n\tconst isSelectionEmpty = Object.keys( selectionStart ).length === 0;\n\tconst noSelection: SelectionNone = {\n\t\ttype: SelectionType.None,\n\t};\n\n\tif ( isSelectionEmpty ) {\n\t\t// Case 1: No selection\n\t\treturn noSelection;\n\t}\n\n\t// When the page initially loads, selectionStart can contain an empty object `{}`.\n\tconst isSelectionInOneBlock =\n\t\tselectionStart.clientId === selectionEnd.clientId;\n\tconst isCursorOnly =\n\t\tisSelectionInOneBlock && selectionStart.offset === selectionEnd.offset;\n\tconst isSelectionAWholeBlock =\n\t\tisSelectionInOneBlock &&\n\t\tselectionStart.offset === undefined &&\n\t\tselectionEnd.offset === undefined;\n\n\tif ( isSelectionAWholeBlock ) {\n\t\t// Case 2: A whole block is selected.\n\t\treturn {\n\t\t\ttype: SelectionType.WholeBlock,\n\t\t\tblockId: selectionStart.clientId,\n\t\t};\n\t} else if ( isCursorOnly ) {\n\t\t// Case 3: Cursor only, no text selected\n\t\tconst cursorPosition = getCursorPosition( selectionStart, yBlocks );\n\n\t\tif ( ! cursorPosition ) {\n\t\t\t// If we can't find the cursor position in block text, treat it as a non-selection.\n\t\t\treturn noSelection;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: SelectionType.Cursor,\n\t\t\tblockId: selectionStart.clientId,\n\t\t\tcursorPosition,\n\t\t};\n\t} else if ( isSelectionInOneBlock ) {\n\t\t// Case 4: Selection in a single block\n\t\tconst cursorStartPosition = getCursorPosition(\n\t\t\tselectionStart,\n\t\t\tyBlocks\n\t\t);\n\t\tconst cursorEndPosition = getCursorPosition( selectionEnd, yBlocks );\n\n\t\tif ( ! cursorStartPosition || ! cursorEndPosition ) {\n\t\t\t// If we can't find the cursor positions in block text, treat it as a non-selection.\n\t\t\treturn noSelection;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: SelectionType.SelectionInOneBlock,\n\t\t\tblockId: selectionStart.clientId,\n\t\t\tcursorStartPosition,\n\t\t\tcursorEndPosition,\n\t\t};\n\t}\n\n\t// Caes 5: Selection in multiple blocks\n\tconst cursorStartPosition = getCursorPosition( selectionStart, yBlocks );\n\tconst cursorEndPosition = getCursorPosition( selectionEnd, yBlocks );\n\tif ( ! cursorStartPosition || ! cursorEndPosition ) {\n\t\t// If we can't find the cursor positions in block text, treat it as a non-selection.\n\t\treturn noSelection;\n\t}\n\n\treturn {\n\t\ttype: SelectionType.SelectionInMultipleBlocks,\n\t\tblockStartId: selectionStart.clientId,\n\t\tblockEndId: selectionEnd.clientId,\n\t\tcursorStartPosition,\n\t\tcursorEndPosition,\n\t};\n}\n\n/**\n * Get the cursor position from a selection.\n *\n * @param selection - The selection.\n * @param blocks - The blocks to search through.\n * @return The cursor position, or null if not found.\n */\nfunction getCursorPosition(\n\tselection: WPBlockSelection,\n\tblocks: YBlocks\n): CursorPosition | null {\n\tconst block = findBlockByClientId( selection.clientId, blocks );\n\tif (\n\t\t! block ||\n\t\t! selection.attributeKey ||\n\t\tundefined === selection.offset\n\t) {\n\t\treturn null;\n\t}\n\n\tconst attributes = block.get( 'attributes' );\n\tconst currentYText = attributes?.get( selection.attributeKey ) as Y.Text;\n\n\tconst relativePosition = Y.createRelativePositionFromTypeIndex(\n\t\tcurrentYText,\n\t\tselection.offset\n\t);\n\n\treturn {\n\t\trelativePosition,\n\t\tabsoluteOffset: selection.offset,\n\t};\n}\n\n/**\n * Find a block by its client ID.\n *\n * @param blockId - The client ID of the block.\n * @param blocks - The blocks to search through.\n * @return The block if found, null otherwise.\n */\nfunction findBlockByClientId(\n\tblockId: string,\n\tblocks: YBlocks\n): YBlock | null {\n\tfor ( const block of blocks ) {\n\t\tif ( block.get( 'clientId' ) === blockId ) {\n\t\t\treturn block;\n\t\t}\n\n\t\tconst innerBlocks = block.get( 'innerBlocks' );\n\n\t\tif ( innerBlocks && innerBlocks.length > 0 ) {\n\t\t\tconst innerBlock = findBlockByClientId( blockId, innerBlocks );\n\n\t\t\tif ( innerBlock ) {\n\t\t\t\treturn innerBlock;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Check if two selection states are equal.\n *\n * @param selection1 - The first selection state.\n * @param selection2 - The second selection state.\n * @return True if the selection states are equal, false otherwise.\n */\nexport function areSelectionsStatesEqual(\n\tselection1: SelectionState,\n\tselection2: SelectionState\n): boolean {\n\tif ( selection1.type !== selection2.type ) {\n\t\treturn false;\n\t}\n\n\tswitch ( selection1.type ) {\n\t\tcase SelectionType.None:\n\t\t\treturn true;\n\n\t\tcase SelectionType.Cursor:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t\t( selection2 as SelectionCursor ).blockId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorPosition,\n\t\t\t\t\t( selection2 as SelectionCursor ).cursorPosition\n\t\t\t\t)\n\t\t\t);\n\n\t\tcase SelectionType.SelectionInOneBlock:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).blockId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorStartPosition,\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).cursorStartPosition\n\t\t\t\t) &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorEndPosition,\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).cursorEndPosition\n\t\t\t\t)\n\t\t\t);\n\n\t\tcase SelectionType.SelectionInMultipleBlocks:\n\t\t\treturn (\n\t\t\t\tselection1.blockStartId ===\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks ).blockStartId &&\n\t\t\t\tselection1.blockEndId ===\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks ).blockEndId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorStartPosition,\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks )\n\t\t\t\t\t\t.cursorStartPosition\n\t\t\t\t) &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorEndPosition,\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks )\n\t\t\t\t\t\t.cursorEndPosition\n\t\t\t\t)\n\t\t\t);\n\t\tcase SelectionType.WholeBlock:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t( selection2 as SelectionWholeBlock ).blockId\n\t\t\t);\n\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Check if two cursor positions are equal.\n *\n * @param cursorPosition1 - The first cursor position.\n * @param cursorPosition2 - The second cursor position.\n * @return True if the cursor positions are equal, false otherwise.\n */\nfunction areCursorPositionsEqual(\n\tcursorPosition1: CursorPosition,\n\tcursorPosition2: CursorPosition\n): boolean {\n\tconst isRelativePositionEqual =\n\t\tJSON.stringify( cursorPosition1.relativePosition ) ===\n\t\tJSON.stringify( cursorPosition2.relativePosition );\n\n\t// Ensure a change in calculated absolute offset results in a treating the cursor as modified.\n\t// This is necessary because Y.Text relative positions can remain the same after text changes.\n\tconst isAbsoluteOffsetEqual =\n\t\tcursorPosition1.absoluteOffset === cursorPosition2.absoluteOffset;\n\n\treturn isRelativePositionEqual && isAbsoluteOffsetEqual;\n}\n"],
|
|
5
|
-
"mappings": ";AAGA,SAAS,GAAG,2BAA2B;AAOvC,SAAS,kBAAkB;
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport { Y, CRDT_RECORD_MAP_KEY } from '@wordpress/sync';\n\n/**\n * Internal dependencies\n */\nimport type { YPostRecord } from './crdt';\nimport type { YBlock, YBlocks } from './crdt-blocks';\nimport { getRootMap } from './crdt-utils';\nimport type {\n\tWPBlockSelection,\n\tSelectionState,\n\tSelectionNone,\n\tSelectionCursor,\n\tSelectionInOneBlock,\n\tSelectionInMultipleBlocks,\n\tSelectionWholeBlock,\n\tCursorPosition,\n} from '../types';\n\n/**\n * The type of selection.\n */\nexport enum SelectionType {\n\tNone = 'none',\n\tCursor = 'cursor',\n\tSelectionInOneBlock = 'selection-in-one-block',\n\tSelectionInMultipleBlocks = 'selection-in-multiple-blocks',\n\tWholeBlock = 'whole-block',\n}\n\n/**\n * Converts WordPress block editor selection to a SelectionState.\n *\n * @param selectionStart - The start position of the selection\n * @param selectionEnd - The end position of the selection\n * @param yDoc - The Yjs document\n * @return The SelectionState\n */\nexport function getSelectionState(\n\tselectionStart: WPBlockSelection,\n\tselectionEnd: WPBlockSelection,\n\tyDoc: Y.Doc\n): SelectionState {\n\tconst ymap = getRootMap< YPostRecord >( yDoc, CRDT_RECORD_MAP_KEY );\n\tconst yBlocks = ymap.get( 'blocks' ) ?? new Y.Array< YBlock >();\n\n\tconst isSelectionEmpty = Object.keys( selectionStart ).length === 0;\n\tconst noSelection: SelectionNone = {\n\t\ttype: SelectionType.None,\n\t};\n\n\tif ( isSelectionEmpty ) {\n\t\t// Case 1: No selection\n\t\treturn noSelection;\n\t}\n\n\t// When the page initially loads, selectionStart can contain an empty object `{}`.\n\tconst isSelectionInOneBlock =\n\t\tselectionStart.clientId === selectionEnd.clientId;\n\tconst isCursorOnly =\n\t\tisSelectionInOneBlock && selectionStart.offset === selectionEnd.offset;\n\tconst isSelectionAWholeBlock =\n\t\tisSelectionInOneBlock &&\n\t\tselectionStart.offset === undefined &&\n\t\tselectionEnd.offset === undefined;\n\n\tif ( isSelectionAWholeBlock ) {\n\t\t// Case 2: A whole block is selected.\n\t\treturn {\n\t\t\ttype: SelectionType.WholeBlock,\n\t\t\tblockId: selectionStart.clientId,\n\t\t};\n\t} else if ( isCursorOnly ) {\n\t\t// Case 3: Cursor only, no text selected\n\t\tconst cursorPosition = getCursorPosition( selectionStart, yBlocks );\n\n\t\tif ( ! cursorPosition ) {\n\t\t\t// If we can't find the cursor position in block text, treat it as a non-selection.\n\t\t\treturn noSelection;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: SelectionType.Cursor,\n\t\t\tblockId: selectionStart.clientId,\n\t\t\tcursorPosition,\n\t\t};\n\t} else if ( isSelectionInOneBlock ) {\n\t\t// Case 4: Selection in a single block\n\t\tconst cursorStartPosition = getCursorPosition(\n\t\t\tselectionStart,\n\t\t\tyBlocks\n\t\t);\n\t\tconst cursorEndPosition = getCursorPosition( selectionEnd, yBlocks );\n\n\t\tif ( ! cursorStartPosition || ! cursorEndPosition ) {\n\t\t\t// If we can't find the cursor positions in block text, treat it as a non-selection.\n\t\t\treturn noSelection;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: SelectionType.SelectionInOneBlock,\n\t\t\tblockId: selectionStart.clientId,\n\t\t\tcursorStartPosition,\n\t\t\tcursorEndPosition,\n\t\t};\n\t}\n\n\t// Caes 5: Selection in multiple blocks\n\tconst cursorStartPosition = getCursorPosition( selectionStart, yBlocks );\n\tconst cursorEndPosition = getCursorPosition( selectionEnd, yBlocks );\n\tif ( ! cursorStartPosition || ! cursorEndPosition ) {\n\t\t// If we can't find the cursor positions in block text, treat it as a non-selection.\n\t\treturn noSelection;\n\t}\n\n\treturn {\n\t\ttype: SelectionType.SelectionInMultipleBlocks,\n\t\tblockStartId: selectionStart.clientId,\n\t\tblockEndId: selectionEnd.clientId,\n\t\tcursorStartPosition,\n\t\tcursorEndPosition,\n\t};\n}\n\n/**\n * Get the cursor position from a selection.\n *\n * @param selection - The selection.\n * @param blocks - The blocks to search through.\n * @return The cursor position, or null if not found.\n */\nfunction getCursorPosition(\n\tselection: WPBlockSelection,\n\tblocks: YBlocks\n): CursorPosition | null {\n\tconst block = findBlockByClientId( selection.clientId, blocks );\n\tif (\n\t\t! block ||\n\t\t! selection.attributeKey ||\n\t\tundefined === selection.offset\n\t) {\n\t\treturn null;\n\t}\n\n\tconst attributes = block.get( 'attributes' );\n\tconst currentYText = attributes?.get( selection.attributeKey ) as Y.Text;\n\n\tconst relativePosition = Y.createRelativePositionFromTypeIndex(\n\t\tcurrentYText,\n\t\tselection.offset\n\t);\n\n\treturn {\n\t\trelativePosition,\n\t\tabsoluteOffset: selection.offset,\n\t};\n}\n\n/**\n * Find a block by its client ID.\n *\n * @param blockId - The client ID of the block.\n * @param blocks - The blocks to search through.\n * @return The block if found, null otherwise.\n */\nfunction findBlockByClientId(\n\tblockId: string,\n\tblocks: YBlocks\n): YBlock | null {\n\tfor ( const block of blocks ) {\n\t\tif ( block.get( 'clientId' ) === blockId ) {\n\t\t\treturn block;\n\t\t}\n\n\t\tconst innerBlocks = block.get( 'innerBlocks' );\n\n\t\tif ( innerBlocks && innerBlocks.length > 0 ) {\n\t\t\tconst innerBlock = findBlockByClientId( blockId, innerBlocks );\n\n\t\t\tif ( innerBlock ) {\n\t\t\t\treturn innerBlock;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Check if two selection states are equal.\n *\n * @param selection1 - The first selection state.\n * @param selection2 - The second selection state.\n * @return True if the selection states are equal, false otherwise.\n */\nexport function areSelectionsStatesEqual(\n\tselection1: SelectionState,\n\tselection2: SelectionState\n): boolean {\n\tif ( selection1.type !== selection2.type ) {\n\t\treturn false;\n\t}\n\n\tswitch ( selection1.type ) {\n\t\tcase SelectionType.None:\n\t\t\treturn true;\n\n\t\tcase SelectionType.Cursor:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t\t( selection2 as SelectionCursor ).blockId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorPosition,\n\t\t\t\t\t( selection2 as SelectionCursor ).cursorPosition\n\t\t\t\t)\n\t\t\t);\n\n\t\tcase SelectionType.SelectionInOneBlock:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).blockId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorStartPosition,\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).cursorStartPosition\n\t\t\t\t) &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorEndPosition,\n\t\t\t\t\t( selection2 as SelectionInOneBlock ).cursorEndPosition\n\t\t\t\t)\n\t\t\t);\n\n\t\tcase SelectionType.SelectionInMultipleBlocks:\n\t\t\treturn (\n\t\t\t\tselection1.blockStartId ===\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks ).blockStartId &&\n\t\t\t\tselection1.blockEndId ===\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks ).blockEndId &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorStartPosition,\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks )\n\t\t\t\t\t\t.cursorStartPosition\n\t\t\t\t) &&\n\t\t\t\tareCursorPositionsEqual(\n\t\t\t\t\tselection1.cursorEndPosition,\n\t\t\t\t\t( selection2 as SelectionInMultipleBlocks )\n\t\t\t\t\t\t.cursorEndPosition\n\t\t\t\t)\n\t\t\t);\n\t\tcase SelectionType.WholeBlock:\n\t\t\treturn (\n\t\t\t\tselection1.blockId ===\n\t\t\t\t( selection2 as SelectionWholeBlock ).blockId\n\t\t\t);\n\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Check if two cursor positions are equal.\n *\n * @param cursorPosition1 - The first cursor position.\n * @param cursorPosition2 - The second cursor position.\n * @return True if the cursor positions are equal, false otherwise.\n */\nfunction areCursorPositionsEqual(\n\tcursorPosition1: CursorPosition,\n\tcursorPosition2: CursorPosition\n): boolean {\n\tconst isRelativePositionEqual =\n\t\tJSON.stringify( cursorPosition1.relativePosition ) ===\n\t\tJSON.stringify( cursorPosition2.relativePosition );\n\n\t// Ensure a change in calculated absolute offset results in a treating the cursor as modified.\n\t// This is necessary because Y.Text relative positions can remain the same after text changes.\n\tconst isAbsoluteOffsetEqual =\n\t\tcursorPosition1.absoluteOffset === cursorPosition2.absoluteOffset;\n\n\treturn isRelativePositionEqual && isAbsoluteOffsetEqual;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["SelectionType", "cursorStartPosition", "cursorEndPosition"]
|
|
7
7
|
}
|
|
@@ -2,7 +2,7 @@ import { AwarenessState } from '@wordpress/sync';
|
|
|
2
2
|
import { areUserInfosEqual } from './utils';
|
|
3
3
|
import type { BaseState } from './types';
|
|
4
4
|
export declare abstract class BaseAwarenessState<State extends BaseState> extends AwarenessState<State> {
|
|
5
|
-
|
|
5
|
+
protected onSetUp(): void;
|
|
6
6
|
/**
|
|
7
7
|
* Set the current user info in the local state.
|
|
8
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-awareness.d.ts","sourceRoot":"","sources":["../../src/awareness/base-awareness.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMjD,OAAO,EAAoB,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,8BAAsB,kBAAkB,CACvC,KAAK,SAAS,SAAS,CACtB,SAAQ,cAAc,CAAE,KAAK,CAAE;
|
|
1
|
+
{"version":3,"file":"base-awareness.d.ts","sourceRoot":"","sources":["../../src/awareness/base-awareness.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMjD,OAAO,EAAoB,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,8BAAsB,kBAAkB,CACvC,KAAK,SAAS,SAAS,CACtB,SAAQ,cAAc,CAAE,KAAK,CAAE;IAChC,SAAS,CAAC,OAAO,IAAI,IAAI;IAIzB;;OAEG;YACW,kBAAkB;CAehC;AAED,eAAO,MAAM,uBAAuB;;CAEnC,CAAC;AAEF,qBAAa,aAAc,SAAQ,kBAAkB,CAAE,SAAS,CAAE;IACjE,SAAS,CAAC,mBAAmB;;MAA2B;CACxD"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Y } from '@wordpress/sync';
|
|
2
2
|
/**
|
|
3
3
|
* Internal dependencies
|
|
4
4
|
*/
|
|
5
5
|
import { BaseAwarenessState } from './base-awareness';
|
|
6
|
-
import type {
|
|
6
|
+
import type { SelectionCursor } from '../types';
|
|
7
|
+
import type { EditorState, PostEditorState, YDocDebugData } from './types';
|
|
7
8
|
export declare class PostEditorAwareness extends BaseAwarenessState<PostEditorState> {
|
|
8
9
|
private kind;
|
|
9
10
|
private name;
|
|
@@ -13,7 +14,7 @@ export declare class PostEditorAwareness extends BaseAwarenessState<PostEditorSt
|
|
|
13
14
|
userInfo: typeof import("./utils").areUserInfosEqual;
|
|
14
15
|
};
|
|
15
16
|
constructor(doc: Y.Doc, kind: string, name: string, postId: number);
|
|
16
|
-
|
|
17
|
+
protected onSetUp(): void;
|
|
17
18
|
/**
|
|
18
19
|
* Subscribe to user selection changes and update the selection state.
|
|
19
20
|
*/
|
|
@@ -34,5 +35,24 @@ export declare class PostEditorAwareness extends BaseAwarenessState<PostEditorSt
|
|
|
34
35
|
* @return True if the editor states are equal, false otherwise.
|
|
35
36
|
*/
|
|
36
37
|
private areEditorStatesEqual;
|
|
38
|
+
/**
|
|
39
|
+
* Get the absolute position index from a selection cursor.
|
|
40
|
+
*
|
|
41
|
+
* @param selection - The selection cursor.
|
|
42
|
+
* @return The absolute position index, or null if not found.
|
|
43
|
+
*/
|
|
44
|
+
getAbsolutePositionIndex(selection: SelectionCursor): number | null;
|
|
45
|
+
/**
|
|
46
|
+
* Type guard to check if a struct is a Y.Item (not Y.GC)
|
|
47
|
+
* @param struct - The struct to check.
|
|
48
|
+
* @return True if the struct is a Y.Item, false otherwise.
|
|
49
|
+
*/
|
|
50
|
+
private isYItem;
|
|
51
|
+
/**
|
|
52
|
+
* Get data for debugging, using the awareness state.
|
|
53
|
+
*
|
|
54
|
+
* @return {YDocDebugData} The debug data.
|
|
55
|
+
*/
|
|
56
|
+
getDebugData(): YDocDebugData;
|
|
37
57
|
}
|
|
38
58
|
//# sourceMappingURL=post-editor-awareness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-editor-awareness.d.ts","sourceRoot":"","sources":["../../src/awareness/post-editor-awareness.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"post-editor-awareness.d.ts","sourceRoot":"","sources":["../../src/awareness/post-editor-awareness.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAIpC;;GAEG;AACH,OAAO,EAAE,kBAAkB,EAA2B,MAAM,kBAAkB,CAAC;AAW/E,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,UAAU,CAAC;AAClE,OAAO,KAAK,EAEX,WAAW,EACX,eAAe,EAEf,aAAa,EACb,MAAM,SAAS,CAAC;AAEjB,qBAAa,mBAAoB,SAAQ,kBAAkB,CAAE,eAAe,CAAE;IAQ5E,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IATf,SAAS,CAAC,mBAAmB;+BAmInB,WAAW,WACX,WAAW,KAClB,OAAO;;MAlIR;gBAGD,GAAG,EAAE,CAAC,CAAC,GAAG,EACF,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM;IAKvB,SAAS,CAAC,OAAO,IAAI,IAAI;IAMzB;;OAEG;IACH,OAAO,CAAC,+BAA+B;IA8DvC;;;;;;OAMG;YACW,6BAA6B;IA+B3C;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAW5B;;;;;OAKG;IACI,wBAAwB,CAC9B,SAAS,EAAE,eAAe,GACxB,MAAM,GAAG,IAAI;IAShB;;;;OAIG;IACH,OAAO,CAAC,OAAO;IAIf;;;;OAIG;IACI,YAAY,IAAI,aAAa;CAiEpC"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { EnhancedState, Y } from '@wordpress/sync';
|
|
1
5
|
/**
|
|
2
6
|
* Internal dependencies
|
|
3
7
|
*/
|
|
4
|
-
import type { SelectionState } from '../
|
|
8
|
+
import type { SelectionState } from '../types';
|
|
5
9
|
import type { User } from '../entity-types';
|
|
6
10
|
export type UserInfo = Pick<User<'view'>, 'id' | 'name' | 'slug' | 'avatar_urls'> & {
|
|
7
11
|
browserType: string;
|
|
@@ -29,4 +33,22 @@ export interface EditorState {
|
|
|
29
33
|
export interface PostEditorState extends BaseState {
|
|
30
34
|
editorState?: EditorState;
|
|
31
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* An enhanced post editor awareness state includes additional metadata about
|
|
38
|
+
* the user and their connection.
|
|
39
|
+
*/
|
|
40
|
+
export type PostEditorAwarenessState = EnhancedState<PostEditorState>;
|
|
41
|
+
export type DebugUserData = Pick<UserInfo, 'name'> & {
|
|
42
|
+
wpUserId: UserInfo['id'];
|
|
43
|
+
};
|
|
44
|
+
export interface YDocDebugData {
|
|
45
|
+
doc: Record<string, unknown>;
|
|
46
|
+
clients: Record<number, Array<SerializableYItem>>;
|
|
47
|
+
userMap: Record<string, DebugUserData>;
|
|
48
|
+
}
|
|
49
|
+
export type SerializableYItemRef = Pick<Y.Item, 'id' | 'length' | 'origin' | 'content'>;
|
|
50
|
+
export type SerializableYItem = Pick<Y.Item, 'id' | 'length' | 'origin' | 'rightOrigin' | 'parent' | 'parentSub' | 'redone' | 'content' | 'info'> & {
|
|
51
|
+
left: SerializableYItemRef | null;
|
|
52
|
+
right: SerializableYItemRef | null;
|
|
53
|
+
};
|
|
32
54
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/awareness/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/awareness/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAExD;;GAEG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,MAAM,QAAQ,GAAG,IAAI,CAC1B,IAAI,CAAE,MAAM,CAAE,EACd,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,CACtC,GAAG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,QAAQ,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,SAAS,EAAE,cAAc,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,SAAS;IACjD,WAAW,CAAC,EAAE,WAAW,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,aAAa,CAAE,eAAe,CAAE,CAAC;AAGxE,MAAM,MAAM,aAAa,GAAG,IAAI,CAAE,QAAQ,EAAE,MAAM,CAAE,GAAG;IACtD,QAAQ,EAAE,QAAQ,CAAE,IAAI,CAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAE,MAAM,EAAE,OAAO,CAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAE,MAAM,EAAE,KAAK,CAAE,iBAAiB,CAAE,CAAE,CAAC;IACtD,OAAO,EAAE,MAAM,CAAE,MAAM,EAAE,aAAa,CAAE,CAAC;CACzC;AAGD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CACtC,CAAC,CAAC,IAAI,EACN,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CACtC,CAAC;AAGF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CACnC,CAAC,CAAC,IAAI,EACJ,IAAI,GACJ,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,SAAS,GACT,MAAM,CACR,GAAG;IACH,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACnC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { PostEditorAwarenessState as ActiveUser, YDocDebugData } from '../awareness/types';
|
|
2
|
+
import type { SelectionCursor } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Hook to get the active users for a post editor.
|
|
5
|
+
*
|
|
6
|
+
* @param postId - The ID of the post.
|
|
7
|
+
* @param postType - The type of the post.
|
|
8
|
+
* @return {ActiveUser[]} The active users.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useActiveUsers(postId: number | null, postType: string | null): ActiveUser[];
|
|
11
|
+
/**
|
|
12
|
+
* Hook to get the absolute position index for a post editor.
|
|
13
|
+
*
|
|
14
|
+
* @param postId - The ID of the post.
|
|
15
|
+
* @param postType - The type of the post.
|
|
16
|
+
* @return {SelectionCursor} The absolute position index.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useGetAbsolutePositionIndex(postId: number | null, postType: string | null): (selection: SelectionCursor) => number | null;
|
|
19
|
+
/**
|
|
20
|
+
* Hook to get data for debugging, using the awareness state.
|
|
21
|
+
*
|
|
22
|
+
* @param postId - The ID of the post.
|
|
23
|
+
* @param postType - The type of the post.
|
|
24
|
+
* @return {YDocDebugData} The debug data.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useGetDebugData(postId: number | null, postType: string | null): YDocDebugData;
|
|
27
|
+
/**
|
|
28
|
+
* Hook to check if the current user is disconnected.
|
|
29
|
+
*
|
|
30
|
+
* @param postId - The ID of the post.
|
|
31
|
+
* @param postType - The type of the post.
|
|
32
|
+
* @return {boolean} Whether the current user is disconnected.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useIsDisconnected(postId: number | null, postType: string | null): boolean;
|
|
35
|
+
//# sourceMappingURL=use-post-editor-awareness-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-post-editor-awareness-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-post-editor-awareness-state.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACX,wBAAwB,IAAI,UAAU,EACtC,aAAa,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AA8EhD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACrB,UAAU,EAAE,CAEd;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAC1C,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACrB,CAAE,SAAS,EAAE,eAAe,KAAM,MAAM,GAAG,IAAI,CAGjD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACrB,aAAa,CAEf;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAGT"}
|
package/build-types/types.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { Y } from '@wordpress/sync';
|
|
5
|
+
/**
|
|
6
|
+
* Internal dependencies
|
|
7
|
+
*/
|
|
8
|
+
import type { SelectionType } from './utils/crdt-user-selections';
|
|
1
9
|
export interface AnyFunction {
|
|
2
10
|
(...args: any[]): any;
|
|
3
11
|
}
|
|
@@ -20,4 +28,37 @@ export interface WPSelection {
|
|
|
20
28
|
selectionEnd: WPBlockSelection;
|
|
21
29
|
selectionStart: WPBlockSelection;
|
|
22
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The position of the cursor.
|
|
33
|
+
*/
|
|
34
|
+
export type CursorPosition = {
|
|
35
|
+
relativePosition: Y.RelativePosition;
|
|
36
|
+
absoluteOffset: number;
|
|
37
|
+
};
|
|
38
|
+
export type SelectionNone = {
|
|
39
|
+
type: SelectionType.None;
|
|
40
|
+
};
|
|
41
|
+
export type SelectionCursor = {
|
|
42
|
+
type: SelectionType.Cursor;
|
|
43
|
+
blockId: string;
|
|
44
|
+
cursorPosition: CursorPosition;
|
|
45
|
+
};
|
|
46
|
+
export type SelectionInOneBlock = {
|
|
47
|
+
type: SelectionType.SelectionInOneBlock;
|
|
48
|
+
blockId: string;
|
|
49
|
+
cursorStartPosition: CursorPosition;
|
|
50
|
+
cursorEndPosition: CursorPosition;
|
|
51
|
+
};
|
|
52
|
+
export type SelectionInMultipleBlocks = {
|
|
53
|
+
type: SelectionType.SelectionInMultipleBlocks;
|
|
54
|
+
blockStartId: string;
|
|
55
|
+
blockEndId: string;
|
|
56
|
+
cursorStartPosition: CursorPosition;
|
|
57
|
+
cursorEndPosition: CursorPosition;
|
|
58
|
+
};
|
|
59
|
+
export type SelectionWholeBlock = {
|
|
60
|
+
type: SelectionType.WholeBlock;
|
|
61
|
+
blockId: string;
|
|
62
|
+
};
|
|
63
|
+
export type SelectionState = SelectionNone | SelectionCursor | SelectionInOneBlock | SelectionInMultipleBlocks | SelectionWholeBlock;
|
|
23
64
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC3B,CAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAI,GAAG,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC3B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,cAAc,EAAE,gBAAgB,CAAC;CACjC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAEzC;;GAEG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,WAAW;IAC3B,CAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAI,GAAG,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC3B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,cAAc,EAAE,gBAAgB,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,gBAAgB,EAAE,CAAC,CAAC,gBAAgB,CAAC;IAYrC,cAAc,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAE3B,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAE7B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,cAAc,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAEjC,IAAI,EAAE,aAAa,CAAC,mBAAmB,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,cAAc,CAAC;IACpC,iBAAiB,EAAE,cAAc,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IAEvC,IAAI,EAAE,aAAa,CAAC,yBAAyB,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,cAAc,CAAC;IACpC,iBAAiB,EAAE,cAAc,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAEjC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GACvB,aAAa,GACb,eAAe,GACf,mBAAmB,GACnB,yBAAyB,GACzB,mBAAmB,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { Y } from '@wordpress/sync';
|
|
5
|
-
import type { WPBlockSelection } from '../types';
|
|
5
|
+
import type { WPBlockSelection, SelectionState } from '../types';
|
|
6
6
|
/**
|
|
7
7
|
* The type of selection.
|
|
8
8
|
*/
|
|
@@ -13,39 +13,6 @@ export declare enum SelectionType {
|
|
|
13
13
|
SelectionInMultipleBlocks = "selection-in-multiple-blocks",
|
|
14
14
|
WholeBlock = "whole-block"
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
17
|
-
* The position of the cursor.
|
|
18
|
-
*/
|
|
19
|
-
export type CursorPosition = {
|
|
20
|
-
relativePosition: Y.RelativePosition;
|
|
21
|
-
absoluteOffset: number;
|
|
22
|
-
};
|
|
23
|
-
export type SelectionNone = {
|
|
24
|
-
type: SelectionType.None;
|
|
25
|
-
};
|
|
26
|
-
export type SelectionCursor = {
|
|
27
|
-
type: SelectionType.Cursor;
|
|
28
|
-
blockId: string;
|
|
29
|
-
cursorPosition: CursorPosition;
|
|
30
|
-
};
|
|
31
|
-
export type SelectionInOneBlock = {
|
|
32
|
-
type: SelectionType.SelectionInOneBlock;
|
|
33
|
-
blockId: string;
|
|
34
|
-
cursorStartPosition: CursorPosition;
|
|
35
|
-
cursorEndPosition: CursorPosition;
|
|
36
|
-
};
|
|
37
|
-
export type SelectionInMultipleBlocks = {
|
|
38
|
-
type: SelectionType.SelectionInMultipleBlocks;
|
|
39
|
-
blockStartId: string;
|
|
40
|
-
blockEndId: string;
|
|
41
|
-
cursorStartPosition: CursorPosition;
|
|
42
|
-
cursorEndPosition: CursorPosition;
|
|
43
|
-
};
|
|
44
|
-
export type SelectionWholeBlock = {
|
|
45
|
-
type: SelectionType.WholeBlock;
|
|
46
|
-
blockId: string;
|
|
47
|
-
};
|
|
48
|
-
export type SelectionState = SelectionNone | SelectionCursor | SelectionInOneBlock | SelectionInMultipleBlocks | SelectionWholeBlock;
|
|
49
16
|
/**
|
|
50
17
|
* Converts WordPress block editor selection to a SelectionState.
|
|
51
18
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crdt-user-selections.d.ts","sourceRoot":"","sources":["../../src/utils/crdt-user-selections.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,CAAC,EAAuB,MAAM,iBAAiB,CAAC;AAQzD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"crdt-user-selections.d.ts","sourceRoot":"","sources":["../../src/utils/crdt-user-selections.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,CAAC,EAAuB,MAAM,iBAAiB,CAAC;AAQzD,OAAO,KAAK,EACX,gBAAgB,EAChB,cAAc,EAOd,MAAM,UAAU,CAAC;AAElB;;GAEG;AACH,oBAAY,aAAa;IACxB,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,mBAAmB,2BAA2B;IAC9C,yBAAyB,iCAAiC;IAC1D,UAAU,gBAAgB;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,cAAc,EAAE,gBAAgB,EAChC,YAAY,EAAE,gBAAgB,EAC9B,IAAI,EAAE,CAAC,CAAC,GAAG,GACT,cAAc,CAgFhB;AAkED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACvC,UAAU,EAAE,cAAc,EAC1B,UAAU,EAAE,cAAc,GACxB,OAAO,CA2DT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/core-data",
|
|
3
|
-
"version": "7.39.0",
|
|
3
|
+
"version": "7.39.1-next.v.0+5aba098fc",
|
|
4
4
|
"description": "Access to and manipulation of core WordPress entities.",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -49,22 +49,22 @@
|
|
|
49
49
|
"build-module/index.mjs"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@wordpress/api-fetch": "^7.39.0",
|
|
53
|
-
"@wordpress/block-editor": "^15.12.0",
|
|
54
|
-
"@wordpress/blocks": "^15.12.0",
|
|
55
|
-
"@wordpress/compose": "^7.39.0",
|
|
56
|
-
"@wordpress/data": "^10.39.0",
|
|
57
|
-
"@wordpress/deprecated": "^4.39.0",
|
|
58
|
-
"@wordpress/element": "^6.39.0",
|
|
59
|
-
"@wordpress/html-entities": "^4.39.0",
|
|
60
|
-
"@wordpress/i18n": "^6.12.0",
|
|
61
|
-
"@wordpress/is-shallow-equal": "^5.39.0",
|
|
62
|
-
"@wordpress/private-apis": "^1.39.0",
|
|
63
|
-
"@wordpress/rich-text": "^7.39.0",
|
|
64
|
-
"@wordpress/sync": "^1.39.0",
|
|
65
|
-
"@wordpress/undo-manager": "^1.39.0",
|
|
66
|
-
"@wordpress/url": "^4.39.0",
|
|
67
|
-
"@wordpress/warning": "^3.39.0",
|
|
52
|
+
"@wordpress/api-fetch": "^7.39.1-next.v.0+5aba098fc",
|
|
53
|
+
"@wordpress/block-editor": "^15.12.1-next.v.0+5aba098fc",
|
|
54
|
+
"@wordpress/blocks": "^15.12.1-next.v.0+5aba098fc",
|
|
55
|
+
"@wordpress/compose": "^7.39.1-next.v.0+5aba098fc",
|
|
56
|
+
"@wordpress/data": "^10.39.1-next.v.0+5aba098fc",
|
|
57
|
+
"@wordpress/deprecated": "^4.39.1-next.v.0+5aba098fc",
|
|
58
|
+
"@wordpress/element": "^6.39.1-next.v.0+5aba098fc",
|
|
59
|
+
"@wordpress/html-entities": "^4.39.1-next.v.0+5aba098fc",
|
|
60
|
+
"@wordpress/i18n": "^6.12.1-next.v.0+5aba098fc",
|
|
61
|
+
"@wordpress/is-shallow-equal": "^5.39.1-next.v.0+5aba098fc",
|
|
62
|
+
"@wordpress/private-apis": "^1.39.1-next.v.0+5aba098fc",
|
|
63
|
+
"@wordpress/rich-text": "^7.39.1-next.v.0+5aba098fc",
|
|
64
|
+
"@wordpress/sync": "^1.39.1-next.v.0+5aba098fc",
|
|
65
|
+
"@wordpress/undo-manager": "^1.39.1-next.v.0+5aba098fc",
|
|
66
|
+
"@wordpress/url": "^4.39.1-next.v.0+5aba098fc",
|
|
67
|
+
"@wordpress/warning": "^3.39.1-next.v.0+5aba098fc",
|
|
68
68
|
"change-case": "^4.1.2",
|
|
69
69
|
"equivalent-key-map": "^0.2.2",
|
|
70
70
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"publishConfig": {
|
|
85
85
|
"access": "public"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "d730f9e00f5462d1b9d2660632850f5f43ccff44"
|
|
88
88
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { resolveSelect } from '@wordpress/data';
|
|
5
5
|
import { AwarenessState } from '@wordpress/sync';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -15,16 +15,14 @@ import type { BaseState } from './types';
|
|
|
15
15
|
export abstract class BaseAwarenessState<
|
|
16
16
|
State extends BaseState,
|
|
17
17
|
> extends AwarenessState< State > {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.setCurrentUserInfo();
|
|
18
|
+
protected onSetUp(): void {
|
|
19
|
+
void this.setCurrentUserInfo();
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
23
|
* Set the current user info in the local state.
|
|
26
24
|
*/
|
|
27
|
-
private setCurrentUserInfo(): void {
|
|
25
|
+
private async setCurrentUserInfo(): Promise< void > {
|
|
28
26
|
const states = this.getStates();
|
|
29
27
|
const otherUserColors = Array.from( states.entries() )
|
|
30
28
|
.filter(
|
|
@@ -35,7 +33,7 @@ export abstract class BaseAwarenessState<
|
|
|
35
33
|
.filter( Boolean );
|
|
36
34
|
|
|
37
35
|
// Get current user info and set it in local state.
|
|
38
|
-
const currentUser =
|
|
36
|
+
const currentUser = await resolveSelect( coreStore ).getCurrentUser();
|
|
39
37
|
const userInfo = generateUserInfo( currentUser, otherUserColors );
|
|
40
38
|
this.setLocalStateField( 'userInfo', userInfo );
|
|
41
39
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { dispatch, select, subscribe } from '@wordpress/data';
|
|
5
|
-
import
|
|
5
|
+
import { Y } from '@wordpress/sync';
|
|
6
6
|
// @ts-ignore No exported types for block editor store selectors.
|
|
7
7
|
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
8
8
|
|
|
@@ -20,8 +20,14 @@ import {
|
|
|
20
20
|
getSelectionState,
|
|
21
21
|
} from '../utils/crdt-user-selections';
|
|
22
22
|
|
|
23
|
-
import type { WPBlockSelection } from '../types';
|
|
24
|
-
import type {
|
|
23
|
+
import type { SelectionCursor, WPBlockSelection } from '../types';
|
|
24
|
+
import type {
|
|
25
|
+
DebugUserData,
|
|
26
|
+
EditorState,
|
|
27
|
+
PostEditorState,
|
|
28
|
+
SerializableYItem,
|
|
29
|
+
YDocDebugData,
|
|
30
|
+
} from './types';
|
|
25
31
|
|
|
26
32
|
export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
27
33
|
protected equalityFieldChecks = {
|
|
@@ -38,8 +44,8 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
38
44
|
super( doc );
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
super.
|
|
47
|
+
protected onSetUp(): void {
|
|
48
|
+
super.onSetUp();
|
|
43
49
|
|
|
44
50
|
this.subscribeToUserSelectionChanges();
|
|
45
51
|
}
|
|
@@ -164,4 +170,101 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
164
170
|
|
|
165
171
|
return areSelectionsStatesEqual( state1.selection, state2.selection );
|
|
166
172
|
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get the absolute position index from a selection cursor.
|
|
176
|
+
*
|
|
177
|
+
* @param selection - The selection cursor.
|
|
178
|
+
* @return The absolute position index, or null if not found.
|
|
179
|
+
*/
|
|
180
|
+
public getAbsolutePositionIndex(
|
|
181
|
+
selection: SelectionCursor
|
|
182
|
+
): number | null {
|
|
183
|
+
return (
|
|
184
|
+
Y.createAbsolutePositionFromRelativePosition(
|
|
185
|
+
selection.cursorPosition.relativePosition,
|
|
186
|
+
this.doc
|
|
187
|
+
)?.index ?? null
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Type guard to check if a struct is a Y.Item (not Y.GC)
|
|
193
|
+
* @param struct - The struct to check.
|
|
194
|
+
* @return True if the struct is a Y.Item, false otherwise.
|
|
195
|
+
*/
|
|
196
|
+
private isYItem( struct: Y.Item | Y.GC ): struct is Y.Item {
|
|
197
|
+
return 'content' in struct;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get data for debugging, using the awareness state.
|
|
202
|
+
*
|
|
203
|
+
* @return {YDocDebugData} The debug data.
|
|
204
|
+
*/
|
|
205
|
+
public getDebugData(): YDocDebugData {
|
|
206
|
+
const ydoc = this.doc;
|
|
207
|
+
|
|
208
|
+
// Manually extract doc data to avoid deprecated toJSON method
|
|
209
|
+
const docData: Record< string, unknown > = Object.fromEntries(
|
|
210
|
+
Array.from( ydoc.share, ( [ key, value ] ) => [
|
|
211
|
+
key,
|
|
212
|
+
value.toJSON(),
|
|
213
|
+
] )
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Build userMap from awareness store (all users seen this session)
|
|
217
|
+
const userMapData = new Map< string, DebugUserData >(
|
|
218
|
+
Array.from( this.getSeenStates().entries() ).map(
|
|
219
|
+
( [ clientId, userState ] ) => [
|
|
220
|
+
String( clientId ),
|
|
221
|
+
{
|
|
222
|
+
name: userState.userInfo.name,
|
|
223
|
+
wpUserId: userState.userInfo.id,
|
|
224
|
+
},
|
|
225
|
+
]
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// Serialize Yjs client items to avoid deep nesting
|
|
230
|
+
const serializableClientItems: Record<
|
|
231
|
+
number,
|
|
232
|
+
Array< SerializableYItem >
|
|
233
|
+
> = {};
|
|
234
|
+
|
|
235
|
+
ydoc.store.clients.forEach( ( structs, clientId ) => {
|
|
236
|
+
// Filter for Y.Item only (skip Y.GC garbage collection structs)
|
|
237
|
+
const items = structs.filter( this.isYItem );
|
|
238
|
+
|
|
239
|
+
serializableClientItems[ clientId ] = items.map( ( item ) => {
|
|
240
|
+
const { left, right, ...rest } = item;
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
...rest,
|
|
244
|
+
left: left
|
|
245
|
+
? {
|
|
246
|
+
id: left.id,
|
|
247
|
+
length: left.length,
|
|
248
|
+
origin: left.origin,
|
|
249
|
+
content: left.content,
|
|
250
|
+
}
|
|
251
|
+
: null,
|
|
252
|
+
right: right
|
|
253
|
+
? {
|
|
254
|
+
id: right.id,
|
|
255
|
+
length: right.length,
|
|
256
|
+
origin: right.origin,
|
|
257
|
+
content: right.content,
|
|
258
|
+
}
|
|
259
|
+
: null,
|
|
260
|
+
};
|
|
261
|
+
} );
|
|
262
|
+
} );
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
doc: docData,
|
|
266
|
+
clients: serializableClientItems,
|
|
267
|
+
userMap: Object.fromEntries( userMapData ),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
167
270
|
}
|