@datalayer/lexical-loro 0.0.7 → 0.2.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer)
4
4
 
5
- # ✍️ 🦜 Lexical Loro - Collaborative Plugin for Lexical with Loro CRDT
5
+ # ✍️ 🦜 Collaborative Plugin for Lexical with Loro CRDT
6
6
 
7
7
  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.
8
8
 
@@ -305,151 +305,38 @@ The examples include:
305
305
 
306
306
  See `src/examples/README.md` for detailed example documentation.
307
307
 
308
- ## Project Structure
309
-
310
- ### Core Components
311
-
312
- ```
313
- src/
314
- ├── LoroCollaborativePlugin.tsx # Main Lexical plugin for collaboration
315
- └── vite-env.d.ts # TypeScript definitions
316
-
317
- lexical_loro/ # Python WebSocket server package
318
- ├── __init__.py # Package exports
319
- ├── server.py # WebSocket server implementation
320
- ├── cli.py # Command line interface
321
- ├── model/
322
- │ └── lexical_model.py # Standalone LexicalModel library
323
- └── tests/ # Python test suite
324
-
325
- docs/
326
- └── LEXICAL_MODEL_GUIDE.md # Comprehensive library documentation
327
-
328
- examples/
329
- ├── memory_only_example.py # Basic LexicalModel usage
330
- ├── file_sync_example.py # File persistence example
331
- ├── collaboration_example.py # Collaborative editing simulation
332
- └── README.md # Examples documentation
333
-
334
- pyproject.toml # Python package configuration
335
- ```
336
-
337
- ### Examples Directory
338
-
339
- ```
340
- src/examples/ # Complete demo application
341
- ├── App.tsx # Demo app with dual editors
342
- ├── LexicalCollaborativeEditor.tsx # Rich text editor example
343
- ├── TextAreaCollaborativeEditor.tsx # Simple text editor example
344
- ├── ServerSelector.tsx # Server selection UI
345
- ├── LexicalToolbar.tsx # Rich text toolbar
346
- ├── main.tsx # Demo app entry point
347
- └── *.css # Styling for examples
348
-
349
- servers/
350
- └── server.ts # Node.js server (for comparison)
351
- ```
352
-
353
- ### Archive
354
-
355
- ```
356
- src/archive/ # Historical plugin implementations
357
- ├── LoroCollaborativePlugin0.tsx # Previous versions for reference
358
- ├── LoroCollaborativePlugin1.tsx
359
- ├── LoroCollaborativePlugin2.tsx
360
- ├── LoroCollaborativePlugin3.tsx
361
- ├── LoroCollaborativePlugin4.tsx
362
- └── LoroCollaborativePlugin5.tsx
363
- ```
364
-
365
308
  ## Architecture
366
309
 
367
- For detailed architecture documentation, see [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
368
-
369
- ### System Overview
370
-
371
- The collaboration system consists of three main components:
372
-
373
- 1. **LoroCollaborativePlugin** (Client-side) - Lexical integration
374
- 2. **LoroWebSocketServer** (Server-side) - Real-time synchronization
375
- 3. **LexicalModel** (Standalone Library) - Independent document model
376
-
377
- ### Data Flow
378
-
379
310
  ```
380
- User Types → Lexical Editor → Plugin → Loro CRDT → WebSocket
381
-
382
- WebSocket ← Loro CRDT ← Plugin ← Lexical Editor ← Other Users
311
+ EDITOR 1 EDITOR 2
312
+
313
+ loro loro
314
+ - node(data: root(1)) - node(data: root(12))
315
+ - node(data: element(2)) - node(data: element(22))
316
+ - node(data: text(3)) - node(data: text(13))
317
+ - node(data: counter(4)) - node(data: counter(4))
318
+
319
+ <---- loro updates via websocket ------>
320
+ <---- loro node ids are the same ------>
321
+ <---- lexical node keys are different ------>
322
+
323
+ lexical lexical
324
+ - root(1) - root(12)
325
+ - element(2) - element(22)
326
+ - text(3) - text(13)
327
+ - counter(4) - counter(49)
383
328
  ```
384
329
 
385
- ## Configuration
386
-
387
- For detailed configuration options, see [`docs/API.md`](docs/API.md).
388
-
389
- ### Quick Configuration
390
-
391
- ```tsx
392
- // Plugin configuration
393
- <LoroCollaborativePlugin
394
- websocketUrl="ws://localhost:8081"
395
- docId="my-document"
396
- username="user123"
397
- debug={true}
398
- />
399
- ```
400
-
401
- ```bash
402
- # Server configuration
403
- lexical-loro-server --port 8081 --log-level DEBUG
404
- ```
405
-
406
- ## Development
407
-
408
- For comprehensive development guidelines, see [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md).
409
-
410
- ### Quick Start
411
-
412
- ```bash
413
- # Install dependencies
414
- npm install
415
- pip install -e ".[dev]"
416
-
417
- # Run tests
418
- npm test
419
- npm run test:py
420
-
421
- # Start development server
422
- lexical-loro-server --log-level DEBUG
423
- ```
424
-
425
- ## Contributing
426
-
427
- We welcome contributions! Please see [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) for detailed guidelines.
428
-
429
- ### Quick Contributing Guide
430
-
431
- 1. Fork the repository
432
- 2. Create a feature branch
433
- 3. Focus changes on core components
434
- 4. Add tests for new functionality
435
- 5. Update documentation as needed
436
- 6. Submit a pull request
330
+ ## Examples
437
331
 
438
- ## Documentation
332
+ Loro examples
439
333
 
440
- - **[API Documentation](docs/API.md)** - Complete API reference
441
- - **[Initialization Guide](docs/INITIALIZATION_GUIDE.md)** - Best practices for setup
442
- - **[Architecture](docs/ARCHITECTURE.md)** - System design and data flow
443
- - **[Development Guide](docs/DEVELOPMENT.md)** - Contributing and development setup
444
- - **[LexicalModel Guide](docs/LEXICAL_MODEL_GUIDE.md)** - Standalone library documentation
334
+ - http://localhost:3000/?isCollab=true
445
335
 
446
- ## License
336
+ - http://localhost:3000/split/?isCollab=true
447
337
 
448
- This project is open source and available under the [MIT License](LICENSE).
338
+ Y.js examples (for reference)
449
339
 
450
- ## Acknowledgments
340
+ - http://localhost:3000/?isCollab=true&useYjs=true
451
341
 
452
- - [Loro CRDT](https://loro.dev/) - The CRDT library powering collaborative editing
453
- - [Lexical](https://lexical.dev/) - Facebook's extensible text editor framework
454
- - [React](https://reactjs.org/) - UI library for plugin hooks
455
- - [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) - Real-time communication
342
+ - http://localhost:3000/split/?isCollab=true&useYjs=true
package/package.json CHANGED
@@ -1,93 +1,124 @@
1
1
  {
2
2
  "name": "@datalayer/lexical-loro",
3
- "private": false,
4
- "version": "0.0.7",
5
- "type": "module",
6
- "description": "Collaborative editing solution for Lexical based on Loro CRDT",
3
+ "version": "0.2.1",
7
4
  "main": "lib/index.js",
8
- "types": "lib/index.d.ts",
9
5
  "files": [
10
- "lib/**/*.*"
6
+ "lib/**/*.{css,d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
7
+ "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
8
+ "schema/*.json"
11
9
  ],
12
10
  "keywords": [
13
- "lexical",
14
- "loro",
15
- "crdt",
16
- "collaborative",
17
- "editor",
18
- "plugin",
19
- "react",
20
- "rich-editor"
11
+ "jupyter",
12
+ "jupyterlab",
13
+ "react"
21
14
  ],
22
15
  "repository": {
23
16
  "type": "git",
24
17
  "url": "https://github.com/datalayer/lexical-loro.git"
25
18
  },
26
- "author": "Datalayer <info@datalayer.io>",
27
- "license": "MIT",
19
+ "sideEffects": [
20
+ "style/*.css",
21
+ "src/**/*.css",
22
+ "style/index.js"
23
+ ],
24
+ "styleModule": "style/index.js",
28
25
  "publishConfig": {
29
26
  "access": "public"
30
27
  },
31
28
  "scripts": {
32
29
  "build": "tsc -b && vite build",
30
+ "dev": "cross-env NODE_ENV=development concurrently \"npm:server:py:ws\" \"npm:server:py:mcp\" \"vite --host --port 3000\"",
31
+ "build:tsc": "tsc",
32
+ "build:dev": "vite build",
33
+ "build:prod": "vite build --mode production",
34
+ "server:loro": "fkill -s :1235 && cross-env HOST=localhost PORT=1235 YPERSISTENCE=./.wss-loro tsx src/collab/loro/servers/ws/server.ts",
35
+ "server:yjs": "fkill -s :1234 && cross-env HOST=localhost PORT=1234 YPERSISTENCE=./.wss-yjs tsx src/collab/yjs/servers/ws/server.ts",
36
+ "server:yjs:dist": "cross-env HOST=localhost PORT=1234 YPERSISTENCE=./.yjs-wss-db npx y-websocket",
37
+ "test": "npm run test:js && npm run test:py",
38
+ "test:watch": "jest --watch",
39
+ "test:coverage": "npm run test:js:coverage && npm run test:py:coverage",
40
+ "test:js:coverage": "jest --coverage",
41
+ "preview": "vite preview",
42
+ "watch:lib": "tsc -b -w",
33
43
  "clean": "rimraf dist lib",
34
- "dev": "npm run example",
35
44
  "dev:all:js": "npm run example:js",
36
45
  "dev:all:py": "npm run example:py",
37
46
  "dev:vite": "npm run example:vite",
38
- "example": "concurrently \"npm run server\" \"npm run server:py\" \"npm run server:mcp\" \"npm run example:vite\"",
47
+ "example": "concurrently \"npm run server\" \"npm run server:py\" \"npm run server:py:mcp\" \"npm run example:vite\"",
39
48
  "example:js": "concurrently \"npm run server\" \"npm run example:vite\"",
40
49
  "example:py": "concurrently \"npm run server:py:dev\" \"npm run example:vite\"",
41
50
  "example:vite": "vite",
42
51
  "lint": "eslint .",
43
- "preview": "vite preview",
44
52
  "server": "tsx dev/node-server.ts",
45
53
  "server:py": "lexical-loro-server",
46
54
  "server:py:dev": "python3 -m lexical_loro.cli",
47
- "server:mcp": "python3 -m lexical_loro.mcp start --transport streamable-http --port 3001 --host 0.0.0.0",
48
- "test": "vitest",
55
+ "server:py:ws": "python3 -m lexical_loro.cli --host localhost --port 3002 --autosave-interval 5",
56
+ "server:py:mcp": "python3 -m lexical_loro.mcp start --transport streamable-http --host 0.0.0.0 --port 3001 --websocket-url ws://localhost:3002 --documents-path ./documents --log-level DEBUG",
49
57
  "test:js": "vitest run",
50
- "test:py": "python3 -m pytest lexical_loro/tests/ -v",
51
- "test:py:coverage": "python3 -m pytest lexical_loro/tests/ --cov=. --cov-report=html --cov-report=term",
52
- "test:py:watch": "python3 -m pytest lexical_loro/tests/ -v --tb=short -f",
53
- "watch:lib": "tsc -b -w"
58
+ "test:py": "python3 -m pytest tests/ -v",
59
+ "test:py:coverage": "python3 -m pytest tests/ --cov=lexical_loro --cov-report=html --cov-report=term",
60
+ "test:py:watch": "python3 -m pytest tests/ -v --tb=short -f"
54
61
  },
55
62
  "dependencies": {
56
- "loro-crdt": "^1.5.10",
57
- "react": "^18.3.1",
58
- "react-dom": "^18.3.1",
59
- "lexical": "^0.33.1",
60
- "@lexical/react": "^0.33.1",
61
- "@lexical/selection": "^0.33.1"
63
+ "@datalayer/primer-addons": "^1.0.8",
64
+ "@floating-ui/react": "^0.27.8",
65
+ "@lexical/clipboard": "^0.35.0",
66
+ "@lexical/code": "^0.35.0",
67
+ "@lexical/code-shiki": "^0.35.0",
68
+ "@lexical/file": "^0.35.0",
69
+ "@lexical/hashtag": "^0.35.0",
70
+ "@lexical/headless": "^0.35.0",
71
+ "@lexical/link": "^0.35.0",
72
+ "@lexical/list": "^0.35.0",
73
+ "@lexical/mark": "^0.35.0",
74
+ "@lexical/overflow": "^0.35.0",
75
+ "@lexical/plain-text": "^0.35.0",
76
+ "@lexical/react": "^0.35.0",
77
+ "@lexical/rich-text": "^0.35.0",
78
+ "@lexical/selection": "^0.35.0",
79
+ "@lexical/table": "^0.35.0",
80
+ "@lexical/utils": "^0.35.0",
81
+ "date-fns": "^4.1.0",
82
+ "katex": "^0.16.10",
83
+ "lexical": "^0.35.0",
84
+ "lodash-es": "^4.17.21",
85
+ "loro-crdt": "^1.8.0",
86
+ "prettier": "^2.8.8",
87
+ "react": "^18.2.0",
88
+ "react-day-picker": "^9.7.0",
89
+ "react-dom": "^18.2.0",
90
+ "react-error-boundary": "^3.1.4",
91
+ "y-websocket": "3.0.0",
92
+ "yjs": ">=13.5.42"
62
93
  },
63
94
  "devDependencies": {
64
- "@eslint/js": "^9.30.1",
65
- "@types/react": "18.3.20",
66
- "@types/react-dom": "18.3.6",
67
- "@types/ws": "^8.18.1",
68
- "@vitejs/plugin-react": "^4.6.0",
95
+ "@babel/plugin-transform-flow-strip-types": "^7.24.7",
96
+ "@babel/preset-react": "^7.24.7",
97
+ "@excalidraw/excalidraw": "^0.18.0",
98
+ "@rollup/plugin-babel": "^6.0.4",
99
+ "@rollup/plugin-commonjs": "^25.0.7",
100
+ "@types/jest": "^29.5.12",
101
+ "@types/lodash-es": "^4.14.182",
102
+ "@types/prettier": "^3.0.0",
103
+ "@vitejs/plugin-react": "^4.2.1",
69
104
  "concurrently": "^9.2.0",
105
+ "cross-env": "^7.0.3",
70
106
  "eslint": "^9.30.1",
71
107
  "eslint-plugin-react-hooks": "^5.2.0",
72
108
  "eslint-plugin-react-refresh": "^0.4.20",
73
- "globals": "^16.3.0",
74
- "@lexical/code": "^0.33.1",
75
- "@lexical/history": "^0.33.1",
76
- "@lexical/html": "^0.33.1",
77
- "@lexical/list": "^0.33.1",
78
- "@lexical/mark": "^0.33.1",
79
- "@lexical/plain-text": "^0.33.1",
80
- "@lexical/rich-text": "^0.33.1",
81
- "@lexical/table": "^0.33.1",
82
- "@lexical/utils": "^0.33.1",
83
- "rimraf": "^6.0.1",
84
- "tsx": "^4.20.3",
85
- "typescript": "~5.8.3",
86
- "typescript-eslint": "^8.35.1",
87
- "vite": "^7.0.4",
88
- "vitest": "^2.1.4",
109
+ "fkill-cli": "^8.0.0",
110
+ "jest": "^29.7.0",
111
+ "jest-environment-jsdom": "^29.7.0",
112
+ "prettier": "^3.3.2",
113
+ "rollup-plugin-copy": "^3.5.0",
114
+ "ts-jest": "^29.1.2",
115
+ "ts-node": "^10.9.2",
116
+ "tsx": "^4.20.5",
117
+ "typescript": "^5.9.2",
118
+ "vite": "^7.0.0",
119
+ "vite-plugin-static-copy": "^3.1.2",
89
120
  "vite-plugin-top-level-await": "^1.6.0",
90
121
  "vite-plugin-wasm": "^3.5.0",
91
- "ws": "^8.18.3"
122
+ "vitest": "^3.2.4"
92
123
  }
93
124
  }
@@ -1,39 +0,0 @@
1
- import { type LexicalEditor } from 'lexical';
2
- /**
3
- * DiffMerge System for Lexical Editor
4
- *
5
- * This module provides sophisticated differential updates for Lexical editor states,
6
- * preventing wholesale state replacement that would destroy React decorator nodes
7
- * like YouTube embeds, counters, and other custom components.
8
- *
9
- * Key Features:
10
- * - Selective node updates (only changed content)
11
- * - Decorator node preservation (YouTube, Counter nodes remain untouched)
12
- * - Table structure support with cell-level updates
13
- * - Graceful fallback for unsupported operations
14
- * - Deep content comparison to minimize unnecessary updates
15
- *
16
- * Usage:
17
- * ```typescript
18
- * const success = applyDifferentialUpdate(editor, newState, 'collaboration');
19
- * if (!success) {
20
- * // Fall back to setEditorState if differential update fails
21
- * editor.setEditorState(newState);
22
- * }
23
- * ```
24
- */
25
- interface EditorStateData {
26
- root: {
27
- type: 'root';
28
- children: any[];
29
- direction?: string | null;
30
- format?: number;
31
- indent?: number;
32
- version?: number;
33
- };
34
- }
35
- /**
36
- * Main function to apply differential updates to the editor
37
- */
38
- export declare function applyDifferentialUpdate(editor: LexicalEditor, newStateData: EditorStateData | any, source?: string): boolean;
39
- export {};