lexical-loro 1.0.6__tar.gz
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.
- lexical_loro-1.0.6/.gitignore +49 -0
- lexical_loro-1.0.6/LICENSE +21 -0
- lexical_loro-1.0.6/PKG-INFO +387 -0
- lexical_loro-1.0.6/README.md +342 -0
- lexical_loro-1.0.6/dev/README.md +5 -0
- lexical_loro-1.0.6/dev/debug/README.md +5 -0
- lexical_loro-1.0.6/docs/README.md +109 -0
- lexical_loro-1.0.6/examples/README.md +213 -0
- lexical_loro-1.0.6/lexical_loro/__init__.py +11 -0
- lexical_loro-1.0.6/lexical_loro/__version__.py +4 -0
- lexical_loro-1.0.6/lexical_loro/cli.py +68 -0
- lexical_loro-1.0.6/lexical_loro/client.py +15 -0
- lexical_loro-1.0.6/lexical_loro/constants.py +21 -0
- lexical_loro-1.0.6/lexical_loro/mcp/README.md +57 -0
- lexical_loro-1.0.6/lexical_loro/mcp/__init__.py +12 -0
- lexical_loro-1.0.6/lexical_loro/mcp/__main__.py +9 -0
- lexical_loro-1.0.6/lexical_loro/mcp/server.py +833 -0
- lexical_loro-1.0.6/lexical_loro/model/__init__.py +7 -0
- lexical_loro-1.0.6/lexical_loro/model/document_manager.py +900 -0
- lexical_loro-1.0.6/lexical_loro/model/lexical_converter.py +591 -0
- lexical_loro-1.0.6/lexical_loro/model/lexical_loro.py +1430 -0
- lexical_loro-1.0.6/lexical_loro/model/node_mapper.py +369 -0
- lexical_loro-1.0.6/lexical_loro/websocket/__init__.py +8 -0
- lexical_loro-1.0.6/lexical_loro/websocket/__main__.py +10 -0
- lexical_loro-1.0.6/lexical_loro/websocket/server.py +1382 -0
- lexical_loro-1.0.6/pyproject.toml +158 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.wss-loro
|
|
3
|
+
.wss-yjs
|
|
4
|
+
build
|
|
5
|
+
node_modules
|
|
6
|
+
package-lock.json
|
|
7
|
+
tsconfig.tsbuildinfo
|
|
8
|
+
|
|
9
|
+
# Logs
|
|
10
|
+
logs
|
|
11
|
+
*.log
|
|
12
|
+
npm-debug.log*
|
|
13
|
+
yarn-debug.log*
|
|
14
|
+
yarn-error.log*
|
|
15
|
+
pnpm-debug.log*
|
|
16
|
+
lerna-debug.log*
|
|
17
|
+
|
|
18
|
+
*-lock.json
|
|
19
|
+
|
|
20
|
+
node_modules
|
|
21
|
+
lib
|
|
22
|
+
dist
|
|
23
|
+
dist-ssr
|
|
24
|
+
*.local
|
|
25
|
+
|
|
26
|
+
.coverage
|
|
27
|
+
.pytest_cache
|
|
28
|
+
htmlcov
|
|
29
|
+
dist
|
|
30
|
+
|
|
31
|
+
__pycache__
|
|
32
|
+
lexical_loro/__version__.py
|
|
33
|
+
|
|
34
|
+
# Editor directories and files
|
|
35
|
+
.vscode/*
|
|
36
|
+
!.vscode/extensions.json
|
|
37
|
+
.idea
|
|
38
|
+
.DS_Store
|
|
39
|
+
*.suo
|
|
40
|
+
*.ntvs*
|
|
41
|
+
*.njsproj
|
|
42
|
+
*.sln
|
|
43
|
+
*.sw?
|
|
44
|
+
|
|
45
|
+
.models
|
|
46
|
+
|
|
47
|
+
__tests__/*.json
|
|
48
|
+
|
|
49
|
+
documents
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Copyright (c) 2021-2023 Datalayer, Inc.
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lexical-loro
|
|
3
|
+
Version: 1.0.6
|
|
4
|
+
Dynamic: Author
|
|
5
|
+
Dynamic: Author-email
|
|
6
|
+
Dynamic: Summary
|
|
7
|
+
Dynamic: Keywords
|
|
8
|
+
Project-URL: Homepage, https://github.com/datalayer/lexical-loro
|
|
9
|
+
Project-URL: Documentation, https://github.com/datalayer/lexical-loro#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/datalayer/lexical-loro.git
|
|
11
|
+
Project-URL: Issues, https://github.com/datalayer/lexical-loro/issues
|
|
12
|
+
License: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Requires-Dist: click>=8.0.0
|
|
28
|
+
Requires-Dist: loro>=1.6.0
|
|
29
|
+
Requires-Dist: mcp>=2
|
|
30
|
+
Requires-Dist: starlette>=0.27
|
|
31
|
+
Requires-Dist: uvicorn>=0.23.1
|
|
32
|
+
Requires-Dist: websockets>=12.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
40
|
+
Provides-Extra: test
|
|
41
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
|
|
42
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
|
|
43
|
+
Requires-Dist: pytest>=7.0; extra == 'test'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
[](https://datalayer.io)
|
|
47
|
+
|
|
48
|
+
[](https://github.com/sponsors/datalayer)
|
|
49
|
+
|
|
50
|
+
# ✍️ 🦜 Collaborative Plugin for Lexical with Loro CRDT
|
|
51
|
+
|
|
52
|
+
A collaborative editing plugin for [Lexical](https://github.com/facebook/lexical) Rich Editor built with [Loro](https://github.com/loro-dev) CRDT, providing real-time collaborative editing capabilities with conflict-free synchronization.
|
|
53
|
+
|
|
54
|
+
## Core Components
|
|
55
|
+
|
|
56
|
+
This package provides three main components for building collaborative text editors:
|
|
57
|
+
|
|
58
|
+
1. **`LoroCollaborativePlugin.tsx`** - A Lexical plugin that integrates Loro CRDT for real-time collaborative editing
|
|
59
|
+
2. **`LexicalModel` Python Library** - A standalone document model for Lexical content with CRDT capabilities
|
|
60
|
+
3. **`lexical-loro` WebSocket Server** - A Python server using [loro-py](https://github.com/loro-dev/loro-py) for real-time collaboration
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
### Using the Lexical Plugin
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { LoroCollaborativePlugin } from './src/LoroCollaborativePlugin';
|
|
68
|
+
|
|
69
|
+
function MyEditor() {
|
|
70
|
+
return (
|
|
71
|
+
<LexicalComposer initialConfig={editorConfig}>
|
|
72
|
+
<RichTextPlugin />
|
|
73
|
+
<LoroCollaborativePlugin
|
|
74
|
+
websocketUrl="ws://localhost:8081"
|
|
75
|
+
docId="my-document"
|
|
76
|
+
username="user1"
|
|
77
|
+
/>
|
|
78
|
+
</LexicalComposer>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Using the LexicalModel Library
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from lexical_loro import LexicalModel
|
|
87
|
+
|
|
88
|
+
# Create a new document
|
|
89
|
+
model = LexicalModel.create_document("my-document")
|
|
90
|
+
|
|
91
|
+
# Add content
|
|
92
|
+
model.add_block({
|
|
93
|
+
"text": "My Document",
|
|
94
|
+
"format": 0,
|
|
95
|
+
"style": ""
|
|
96
|
+
}, "heading1")
|
|
97
|
+
|
|
98
|
+
model.add_block({
|
|
99
|
+
"text": "This is a paragraph.",
|
|
100
|
+
"format": 0,
|
|
101
|
+
"style": ""
|
|
102
|
+
}, "paragraph")
|
|
103
|
+
|
|
104
|
+
# Save to file
|
|
105
|
+
model.save_to_file("document.json")
|
|
106
|
+
|
|
107
|
+
# Load from file
|
|
108
|
+
loaded_model = LexicalModel.load_from_file("document.json")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Using the Python Server
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Install the Python package
|
|
115
|
+
pip install -e .
|
|
116
|
+
|
|
117
|
+
# Start the server
|
|
118
|
+
lexical-loro-server --port 8081
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Examples
|
|
122
|
+
|
|
123
|
+
For complete working examples, see the `src/examples/` directory which contains:
|
|
124
|
+
- Full React application with dual editor support
|
|
125
|
+
- Server selection interface
|
|
126
|
+
- Connection status indicators
|
|
127
|
+
- Rich text formatting examples
|
|
128
|
+
|
|
129
|
+
**DISCLAIMER** Collaborative Cursors still need fixes, see [this issue](https://github.com/datalayer/lexical-loro/issues/1).
|
|
130
|
+
|
|
131
|
+
<div align="center" style="text-align: center">
|
|
132
|
+
<img alt="" src="https://assets.datalayer.tech/lexical-loro.gif" />
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
## Core Features
|
|
136
|
+
|
|
137
|
+
- 🔄 **Real-time Collaboration**: Multiple users can edit the same document simultaneously
|
|
138
|
+
- 🚀 **Conflict-free**: Uses Loro CRDT to automatically resolve conflicts
|
|
139
|
+
- 📝 **Lexical Integration**: Seamless integration with Lexical rich text editor
|
|
140
|
+
- 📚 **Standalone Library**: Use LexicalModel independently for document management
|
|
141
|
+
- 🌐 **WebSocket Server**: Python server for maintaining document state
|
|
142
|
+
- 📡 **Connection Management**: Robust WebSocket connection handling
|
|
143
|
+
- ✨ **Rich Text Support**: Preserves formatting during collaborative editing
|
|
144
|
+
- 💾 **Serialization**: JSON export/import and file persistence
|
|
145
|
+
- 🔧 **Extensible**: Plugin-based architecture for easy customization
|
|
146
|
+
|
|
147
|
+
## Technology Stack
|
|
148
|
+
|
|
149
|
+
**Core Dependencies:**
|
|
150
|
+
- **Lexical**: v0.33.1 (Facebook's extensible text editor framework)
|
|
151
|
+
- **Loro CRDT**: v1.5.10 (Conflict-free replicated data types)
|
|
152
|
+
- **React**: 18/19 (for plugin hooks and components)
|
|
153
|
+
- **Python**: 3.8+ with loro-py and websockets
|
|
154
|
+
|
|
155
|
+
**Development Dependencies:**
|
|
156
|
+
- **TypeScript**: For type safety
|
|
157
|
+
- **Vite**: For building and development (examples only)
|
|
158
|
+
- **pytest**: Python testing
|
|
159
|
+
- **ESLint**: Code linting
|
|
160
|
+
|
|
161
|
+
## Installation
|
|
162
|
+
|
|
163
|
+
### Core Plugin
|
|
164
|
+
|
|
165
|
+
The Lexical plugin is a single TypeScript/React component that you can copy into your project:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Copy the plugin file
|
|
169
|
+
cp src/LoroCollaborativePlugin.tsx your-project/src/
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Dependencies required:**
|
|
173
|
+
```bash
|
|
174
|
+
npm install lexical @lexical/react @lexical/selection loro-crdt react react-dom
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Python Server
|
|
178
|
+
|
|
179
|
+
Install the Python WebSocket server:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Install from this repository
|
|
183
|
+
pip install -e .
|
|
184
|
+
|
|
185
|
+
# Or install specific dependencies
|
|
186
|
+
pip install websockets click loro
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Usage
|
|
190
|
+
|
|
191
|
+
### 1. Lexical Plugin Integration
|
|
192
|
+
|
|
193
|
+
Add the plugin to your Lexical editor:
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { LexicalComposer } from '@lexical/react/LexicalComposer';
|
|
197
|
+
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
|
|
198
|
+
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
|
|
199
|
+
import { LoroCollaborativePlugin } from './LoroCollaborativePlugin';
|
|
200
|
+
|
|
201
|
+
const editorConfig = {
|
|
202
|
+
namespace: 'MyEditor',
|
|
203
|
+
theme: {},
|
|
204
|
+
onError: console.error,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
function CollaborativeEditor() {
|
|
208
|
+
return (
|
|
209
|
+
<LexicalComposer initialConfig={editorConfig}>
|
|
210
|
+
<div className="editor-container">
|
|
211
|
+
<RichTextPlugin
|
|
212
|
+
contentEditable={<ContentEditable className="editor-input" />}
|
|
213
|
+
placeholder={<div className="editor-placeholder">Start typing...</div>}
|
|
214
|
+
ErrorBoundary={() => <div>Error occurred</div>}
|
|
215
|
+
/>
|
|
216
|
+
<LoroCollaborativePlugin
|
|
217
|
+
websocketUrl="ws://localhost:8081"
|
|
218
|
+
docId="shared-document"
|
|
219
|
+
username="user123"
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
</LexicalComposer>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 2. Standalone LexicalModel Library
|
|
228
|
+
|
|
229
|
+
Use the LexicalModel library independently for document management:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from lexical_loro import LexicalModel
|
|
233
|
+
|
|
234
|
+
# Create a new document
|
|
235
|
+
model = LexicalModel.create_document("my-document")
|
|
236
|
+
|
|
237
|
+
# Add different types of content
|
|
238
|
+
model.add_block({
|
|
239
|
+
"text": "My Document",
|
|
240
|
+
"format": 0,
|
|
241
|
+
"style": ""
|
|
242
|
+
}, "heading1")
|
|
243
|
+
|
|
244
|
+
model.add_block({
|
|
245
|
+
"text": "This is a paragraph with **bold** text.",
|
|
246
|
+
"format": 0,
|
|
247
|
+
"style": ""
|
|
248
|
+
}, "paragraph")
|
|
249
|
+
|
|
250
|
+
model.add_block({
|
|
251
|
+
"text": "",
|
|
252
|
+
"format": 0,
|
|
253
|
+
"style": ""
|
|
254
|
+
}, "list")
|
|
255
|
+
|
|
256
|
+
# Serialize to JSON
|
|
257
|
+
json_data = model.to_json()
|
|
258
|
+
|
|
259
|
+
# Save to file
|
|
260
|
+
model.save_to_file("document.json")
|
|
261
|
+
|
|
262
|
+
# Load from file
|
|
263
|
+
loaded_model = LexicalModel.load_from_file("document.json")
|
|
264
|
+
|
|
265
|
+
# Access blocks
|
|
266
|
+
for block in loaded_model.get_blocks():
|
|
267
|
+
print(f"{block['type']}: {block.get('text', '')}")
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
For more examples, see:
|
|
271
|
+
- `examples/memory_only_example.py` - Basic document creation and manipulation
|
|
272
|
+
- `examples/file_sync_example.py` - File persistence and batch operations
|
|
273
|
+
- `examples/collaboration_example.py` - Simulating collaborative editing
|
|
274
|
+
- `docs/LEXICAL_MODEL_GUIDE.md` - Comprehensive documentation
|
|
275
|
+
|
|
276
|
+
### 3. Python Server Setup
|
|
277
|
+
|
|
278
|
+
Start the WebSocket server:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Default port (8081)
|
|
282
|
+
lexical-loro-server
|
|
283
|
+
|
|
284
|
+
# Custom port
|
|
285
|
+
lexical-loro-server --port 8082
|
|
286
|
+
|
|
287
|
+
# With debug logging
|
|
288
|
+
lexical-loro-server --port 8081 --log-level DEBUG
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 4. Programmatic Server Usage
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
import asyncio
|
|
295
|
+
from lexical_loro import LoroWebSocketServer
|
|
296
|
+
|
|
297
|
+
async def main():
|
|
298
|
+
server = LoroWebSocketServer(port=8081)
|
|
299
|
+
await server.start()
|
|
300
|
+
print("Server running on ws://localhost:8081")
|
|
301
|
+
|
|
302
|
+
if __name__ == "__main__":
|
|
303
|
+
asyncio.run(main())
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Plugin API
|
|
307
|
+
|
|
308
|
+
For detailed API documentation, see [`docs/API.md`](docs/API.md).
|
|
309
|
+
|
|
310
|
+
### Quick Reference
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
313
|
+
interface LoroCollaborativePluginProps {
|
|
314
|
+
websocketUrl: string; // WebSocket server URL
|
|
315
|
+
docId: string; // Unique document identifier
|
|
316
|
+
username: string; // User identifier
|
|
317
|
+
userColor?: string; // User cursor color (optional)
|
|
318
|
+
debug?: boolean; // Enable debug logging (optional)
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Initialization Best Practices
|
|
323
|
+
|
|
324
|
+
⚠️ **Important**: Always wait for collaboration initialization before enabling other plugins.
|
|
325
|
+
|
|
326
|
+
See [`docs/INITIALIZATION_GUIDE.md`](docs/INITIALIZATION_GUIDE.md) for comprehensive guidance on:
|
|
327
|
+
- Proper plugin ordering
|
|
328
|
+
- Initialization callbacks
|
|
329
|
+
- Error handling
|
|
330
|
+
- Common anti-patterns to avoid
|
|
331
|
+
|
|
332
|
+
## Examples
|
|
333
|
+
|
|
334
|
+
For complete working examples and demonstrations, see the `src/examples/` directory:
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
# Run the example application
|
|
338
|
+
npm install
|
|
339
|
+
make example
|
|
340
|
+
|
|
341
|
+
# This starts both Node.js and Python servers plus a React demo app
|
|
342
|
+
# Open http://localhost:3000/split/index.html to see the 2 editors view
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The examples include:
|
|
346
|
+
- **Complete React App**: Full collaborative editor with UI
|
|
347
|
+
- **Server Selection**: Switch between Node.js and Python backends
|
|
348
|
+
- **Dual Editors**: Simple text area and rich Lexical editor
|
|
349
|
+
- **Real-time Demo**: Multi-user collaboration testing
|
|
350
|
+
|
|
351
|
+
See `src/examples/README.md` for detailed example documentation.
|
|
352
|
+
|
|
353
|
+
## Architecture
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
EDITOR 1 EDITOR 2
|
|
357
|
+
|
|
358
|
+
loro loro
|
|
359
|
+
- node(data: root(1)) - node(data: root(12))
|
|
360
|
+
- node(data: element(2)) - node(data: element(22))
|
|
361
|
+
- node(data: text(3)) - node(data: text(13))
|
|
362
|
+
- node(data: counter(4)) - node(data: counter(4))
|
|
363
|
+
|
|
364
|
+
<---- loro updates via websocket ------>
|
|
365
|
+
<---- loro node ids are the same ------>
|
|
366
|
+
<---- lexical node keys are different ------>
|
|
367
|
+
|
|
368
|
+
lexical lexical
|
|
369
|
+
- root(1) - root(12)
|
|
370
|
+
- element(2) - element(22)
|
|
371
|
+
- text(3) - text(13)
|
|
372
|
+
- counter(4) - counter(49)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Examples
|
|
376
|
+
|
|
377
|
+
Loro examples
|
|
378
|
+
|
|
379
|
+
- http://localhost:3000/?isCollab=true
|
|
380
|
+
|
|
381
|
+
- http://localhost:3000/split/?isCollab=true
|
|
382
|
+
|
|
383
|
+
Y.js examples (for reference)
|
|
384
|
+
|
|
385
|
+
- http://localhost:3000/?isCollab=true&useYjs=true
|
|
386
|
+
|
|
387
|
+
- http://localhost:3000/split/?isCollab=true&useYjs=true
|