与 Loro CRDT 协作插件
协作编辑插件 语法 丰富的编辑器 罗罗 CRDT,提供无冲突同步的实时协作编辑能力.
核心组成部分
这个软件包提供了构建协作文本编辑器的三个主要组成部分:
LoroCollaborativePlugin.tsx- 用于实时协作编辑的Lexical插件LexicalModelPython 库 - 具有CRDT能力的单行文件格式lexical-loroWebSocket 服务器 - 使用 Python 服务器 萝拉平 实时协作
快速启动
使用 Lexic 插件
import { LoroCollaborativePlugin } from './src/LoroCollaborativePlugin';
function MyEditor() {
return (
<LexicalComposer initialConfig={editorConfig}>
<RichTextPlugin />
<LoroCollaborativePlugin
websocketUrl="ws://localhost:8081"
docId="my-document"
username="user1"
/>
</LexicalComposer>
);
}
使用 Lexical Model 图书馆
from lexical_loro import LexicalModel
# Create a new document
model = LexicalModel.create_document("my-document")
# Add content
model.add_block({
"text": "My Document",
"format": 0,
"style": ""
}, "heading1")
model.add_block({
"text": "This is a paragraph.",
"format": 0,
"style": ""
}, "paragraph")
# Save to file
model.save_to_file("document.json")
# Load from file
loaded_model = LexicalModel.load_from_file("document.json")
使用 Python 服务器
# Install the Python package
pip install -e .
# Start the server
lexical-loro-server --port 8081
实例
关于完整的工作实例,见 src/examples/ 包含:
- 双编辑器支持的完全反应应用程序
- 服务器选择界面
- 连接状态指标
- 丰富的文本格式化示例
分会 协作光标还需要修正,参见 这个问题。 。 。 .
核心特征
- 🔄 实时协作:多个用户可以同时编辑同一文档
- 🚀 无冲突: 使用Loro CRDT自动解决冲突
- 📝 语法整合: 与 Lexical 丰富的文本编辑器无缝集成
- 📚 独立图书馆: 文件管理独立使用 Lexical Model
- 🌐 WebSocket 服务器: 用于维护文档状态的 Python 服务器
- 📡 连接管理: 强力 WebSocket 连接处理
- ✨ 丰富的文本支持: 协作编辑时保留格式
- 💾 序列化: JSON 出口/进口和文件持久性
- 🔧 广泛: 方便定制的以插件为基础的架构
技术堆栈
核心依赖:
- 语法: v0.33.1 (Facebook的可扩展文本编辑器框架)
- Loro CRDT 软件: v1.5.10 (无冲突复制数据类型)
- 反应: 18/19 (用于插件钩和组件)
- 蟒: 3.8+ 并带有loro-py和websockets
发展依赖:
- 类型脚本: 类型安全
- 维特:用于建筑和开发(仅提供实例)
- 测试: Python 测试
- ESLint 语录: 代码折叠
安装
核心插件
Lexical 插件是一个单一的 TypeScript/ React 组件, 您可以复制到您的工程中 :
# Copy the plugin file
cp src/LoroCollaborativePlugin.tsx your-project/src/
所需依赖:
npm install lexical @lexical/react @lexical/selection loro-crdt react react-dom
Python 服务器
安装 Python WebSocket 服务器 :
# Install from this repository
pip install -e .
# Or install specific dependencies
pip install websockets click loro
使用量
1. 词汇插件集成
将插件添加到您的 Lexical 编辑器 :
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LoroCollaborativePlugin } from './LoroCollaborativePlugin';
const editorConfig = {
namespace: 'MyEditor',
theme: {},
onError: console.error,
};
function CollaborativeEditor() {
return (
<LexicalComposer initialConfig={editorConfig}>
<div className="editor-container">
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<div className="editor-placeholder">Start typing...</div>}
ErrorBoundary={() => <div>Error occurred</div>}
/>
<LoroCollaborativePlugin
websocketUrl="ws://localhost:8081"
docId="shared-document"
username="user123"
/>
</div>
</LexicalComposer>
);
}
2. 独立的词典模型图书馆
在文档管理中独立使用 Lexical Model 库 :
from lexical_loro import LexicalModel
# Create a new document
model = LexicalModel.create_document("my-document")
# Add different types of content
model.add_block({
"text": "My Document",
"format": 0,
"style": ""
}, "heading1")
model.add_block({
"text": "This is a paragraph with **bold** text.",
"format": 0,
"style": ""
}, "paragraph")
model.add_block({
"text": "",
"format": 0,
"style": ""
}, "list")
# Serialize to JSON
json_data = model.to_json()
# Save to file
model.save_to_file("document.json")
# Load from file
loaded_model = LexicalModel.load_from_file("document.json")
# Access blocks
for block in loaded_model.get_blocks():
print(f"{block['type']}: {block.get('text', '')}")
更多例子见:
examples/memory_only_example.py- 基本文件的创建和操纵examples/file_sync_example.py- 文件持久性和批量操作examples/collaboration_example.py- 模拟协作编辑docs/LEXICAL_MODEL_GUIDE.md- 综合文件
3. Python 服务器设置
启动 WebSocket 服务器 :
# Default port (8081)
lexical-loro-server
# Custom port
lexical-loro-server --port 8082
# With debug logging
lexical-loro-server --port 8081 --log-level DEBUG
4. 程序服务器的使用
import asyncio
from lexical_loro import LoroWebSocketServer
async def main():
server = LoroWebSocketServer(port=8081)
await server.start()
print("Server running on ws://localhost:8081")
if __name__ == "__main__":
asyncio.run(main())
插件 API
关于详细的API文档,参见: docs/API.md。 。 。 .
快速引用
interface LoroCollaborativePluginProps {
websocketUrl: string; // WebSocket server URL
docId: string; // Unique document identifier
username: string; // User identifier
userColor?: string; // User cursor color (optional)
debug?: boolean; // Enable debug logging (optional)
}
初始化最佳做法
⚠️ 重要: 总是等待协作初始化后才能启用其他插件.
见 docs/INITIALIZATION_GUIDE.md 关于以下方面的全面指导意见:
- 适当的插件排序
- 初始化回调
- 处理错误
- 常见的防标剂
实例
完整的工作实例和演示,见 src/examples/ 目录 :
# Run the example application
npm install
make example
# This starts both Node.js and Python servers plus a React demo app
# Open http://localhost:3000/split/index.html to see the 2 editors view
这些例子包括:
- 完整回放应用程序: 与 UI 完全合作编辑器
- 服务器选择: 在节点.js和Python后端之间切换
- 双向编辑器: 简单的文本区和丰富的 Lexical 编辑器
- 实时演示: 多用户协作测试
见 src/examples/README.md 例如,详细的文档.
建筑
EDITOR 1 EDITOR 2
loro loro
- node(data: root(1)) - node(data: root(12))
- node(data: element(2)) - node(data: element(22))
- node(data: text(3)) - node(data: text(13))
- node(data: counter(4)) - node(data: counter(4))
<---- loro updates via websocket ------>
<---- loro node ids are the same ------>
<---- lexical node keys are different ------>
lexical lexical
- root(1) - root(12)
- element(2) - element(22)
- text(3) - text(13)
- counter(4) - counter(49)
实例
Loro 实例
-
http://localhost:3:00/?is Collab=real (英语)
-
http://localhost:3:00/split/?is Collab=real (中文(简体) )
Y.js实例(供参考)
-
http://localhost:3:00/?.isCollab=real&useYjs=real (英语)
-
http://localhost:3:00/split/?.is Collab=real&useYjs=real (英语)