- 前端
- 桌面应用
- AI 应用
- MCP 服务
【免费下载链接】open-pencil
AI-native design editor. Open-source Figma alternative.
OpenPencil 是一款 AI-native 设计编辑器(开源 Figma 替代品),其协作能力完全内置于产品本身:通过 P2P WebRTC 直连、CRDT 自动合并,任何人无需账号即可实时共同编辑同一份设计文档。本文以官方文档 collaboration.md 为主体,结合仓库源码(src/app/collab、tests/app/collab)讲解房间的分享与加入、同步范围、跟随模式以及底层 P2P 与 CRDT 原理,读完后你可以立刻上手多人协同,并理解其数据如何在不经过中心服务器的前提下保持各端一致。
核心概念:房间(Room)
在 OpenPencil 中,协作以“房间”为基本单位。一个房间对应一份被共享的设计文档:
- 分享(Share):只有分享动作才会把当前文档放入房间。被你分享的那个标签页会成为房间的标签页,并始终与该文件绑定。
- 加入(Join):任何拿到链接的人都可以加入房间,无需注册或登录。
- 无服务器:房间不存储在服务器上,文件实际存在于曾经进入过房间的各设备中(见 session.ts 中的本地持久化逻辑)。
- 多房间并行:每个房间标签页持有独立的连接(rooms.ts 中
sessions按标签页管理),所以你可以同时处于多个房间,且都能在后台持续同步。
从源码看,房间会话(RoomSession)封装了一个房间在一枚标签页中的全部状态:文档、在线成员(peers)、连接与本地保存副本(session.ts)。
分享一个房间
- 点击右上角的分享按钮;
- 点击Share this file,链接(
app.openpencil.dev/share/<room-id>)即被复制; - 把链接发送给协作者即可。
对应的前端路由前缀在 route.ts 中定义:SHARE_ROUTE_PREFIX = '/share/',房间 URL 即/share/<room-id>。地址栏会跟随当前激活标签页的房间状态(route.ts),因此刷新页面或复制地址栏 URL 打开的正是屏幕上那份文档。
房间 ID 由 awareness.ts 的generateRoomId()生成,基于ROOM_ID_CHARS与ROOM_ID_LENGTH(见 constants.ts),每次随机抽取字符,因此只有拿到链接的人才能加入。
加入一个房间
- 直接打开链接;
- 或把完整链接(甚至只有房间 ID)粘贴到分享面板的Join输入框、Home 页的Join room…;
- 在电脑上,浏览器还会提示Open in desktop app,通过
openpencil://join链接在桌面版 OpenPencil 中打开该房间。
加入行为由 rooms.ts 的joinRoom()实现:校验房间 ID 合法性(isRoomId)、创建独立的新标签页(createTab()),并在文档到达前先显示加入中的占位页。因此你已打开的文档永远不会被改动。
访客名称
加入后你会立即获得一个自动生成的名称,例如Teal Fox。生成逻辑在 guest-name.ts:从 16 个颜色词(Amber、Azure、Coral、Cobalt、Copper、Crimson、Indigo、Ivory、Jade、Lilac、Olive、Ruby、Saffron、Scarlet、Teal、Violet)与 16 个动物词(Badger、Crane、Dolphin、Falcon、Fox、Gecko、Heron、Ibis、Koala、Lynx、Marten、Otter、Panda、Puffin、Raven、Wombat)中各随机取一组合而成。你可以在分享面板或设置里改成自己的名字,该名字会在所有房间中使用(身份信息通过 awareness 广播,见 session.ts)。
等待与离线文件
房间文件不在服务器上,因此只有当至少一个进过房间的人在线时,房间标签页才能打开文档。在此之前它显示“等待中”,并解释原因;一旦某个持有文件的人加入,文档就会立即打开。
- 如果你曾进过这个房间,会先从本机副本直接打开(快速显示),等其他人回来后再同步你的修改;
- 本机副本由
IndexeddbPersistence(y-indexeddb)保存,键名为op-room-<roomId>(session.ts),并异步等待其加载完成后才判定“已有文档”。
离开房间
分享面板中的Leave room结束你在这个房间的参与:
- 分享过文档的标签页回到“普通文档”状态;
- 加入过房间的标签页保留房间文件作为本地未保存副本(可另存);
- 若文档从未到达,标签页会被关闭(rooms.ts)。
同步范围:哪些内容实时同步
- 文档修改:所有编辑(形状、文本、属性、布局)即时同步;
- 光标:可看到每个协作者指向的位置、名字与颜色;
- 选区:高亮的选中状态对所有人可见;
- Agent:内置 AI 聊天、ACP 与 Pi harness 聊天、以及每个已连接的 MCP 客户端,都会以其正在读取或编辑的图层上的光标形式出现——每个带轮廓的标签显示一个星标(sparkle)和呼号(如Fern)。聊天流式输出 JSX 时,光标会随元素逐个出现并给它们描边。光标与描边使用运行该 Agent 的人的颜色,方便区分是谁的 Agent。只共享名称、类型、模型、状态、所在页、位置与被编辑图层,绝不共享提示词或回复内容。
从实现上看,这些同步通过两类机制完成(session.ts):
- 文档数据:写入
ydoc.getMap('nodes')(图层)与ydoc.getMap('images')(图片二进制),由 Yjs 增量同步; - 存在感(presence):基于
y-protocols/awareness广播用户信息、光标{x, y, pageId, zoom}、选区selection、hasFile(是否持有文档)等字段(session.ts)。
跟随模式(Follow Mode)
- 点击顶部栏某协作者的头像即可跟随其视口:你的画布会平移、缩放以匹配对方的视野,并用对方颜色的边框与一条 “Following …” 提示条标示你正在跟随谁;
- 再次点击头像、按Esc、或自己点击/滚动/缩放/切换页面,都会停止跟随。
跟随 Agent
- 你自己的 Agent(AI 聊天、MCP 客户端如 Claude Code 或 Cursor)在运行时会被自动跟随,确保它们编辑的内容始终在视野内;可在 AI 面板顶部的十字准星按钮关闭;
- 若在 Agent 工作时停止跟随,它会独自工作到结束,下一次运行时才会再次被跟随;
- 头像上的数字表示该用户运行了几个 Agent:悬停可查看每个 Agent 在做什么、在哪一页,点击某 Agent 旁的Follow可保持其正在编辑的页面与图层在视野内;跟随会持续到 Agent 回复之间,并在其离开时停止;
- 头像之后的按钮列出房间内所有成员及其 Agent,支持键盘操作;你自己的头像列出你的 Agent——点击即可重命名——并提供Leave room;
- 分享面板同样列出房间成员及其 Agent 的状态与所在页,双击自己的 Agent 即可重命名。
工作原理:P2P WebRTC + CRDT
直连传输:数据不过中心服务器
协作者之间通过WebRTC直接相连,设计数据直接从浏览器到浏览器(桌面端同理),从不经过中心服务器。信令传输层实现在 trystero.ts:基于 Trystero 的 MQTT 中继(appId 为COLLAB_APP_ID = 'openpencil/2',见 constants.ts)完成对等发现,随后建立 WebRTC 连接,并配置了公开的 STUN/TURN 服务器(Google、Cloudflare 的 STUN 以及 openrelay 的 TURN,含 TCP 传输)以穿透 NAT。
一个值得注意的实现细节:Trystero 约每 5.3 秒向房间的 broker 广播一次存在,因此经过两轮广播加上一次 WebRTC 握手,房间内已有成员即可“认识”新加入者;TRYSTERO_DISCOVERY_MS = 12_000正是对这一发现周期的容错窗口(trystero.ts)。
文档状态:CRDT 自动合并
文档状态使用CRDT(无冲突复制数据类型),并发编辑会自动合并、无需解决冲突。实现上是 Yjs:
- 每个房间会话持有独立的
Y.Doc,图层放入nodes地图、图片放入images地图(session.ts); - 本地编辑由
bindCollabGraphEvents监听node:updated、node:created、node:reparented、node:reordered、node:deleted等编辑器事件,合并为一次本地编辑(LocalEdit),再在单个 Yjs 事务内写入(yjs-sync.ts); - 远端变更由
registerYjsObservers观察nodes与images,反解后应用到场景图(yjs-sync.ts)。测试 concurrent-edits.test.ts、random-edits.test.ts 对并发与随机编辑做了系统性验证。
图层树合并:树形 CRDT
移动和重排图层同样可以合并。每个图层都会记住它曾被移入的每一个父级以及它在兄弟中的位置,每个对等端都依据这份历史推导出相同的图层树——即 Evan Wallace 的树形 CRDT(mutable tree hierarchy)算法。效果:
- 不同人发起的移动、重排、新建图层全部生效;
- 若两人同时移动同一图层,其中一个移动会在所有对等端上胜出;
- 当同一时刻产生的移动会把两个图层互相放入对方内部时,后发生的移动会被撤销;
- 若某图层的新父级恰好在这期间被删除,该图层会回到原位。
版本一致性约束:房间内所有人都需要安装以相同方式记录图层树的 OpenPencil 版本;记录方式不同的版本之间互不可见对方的房间。共享的树格式标记通过 awareness 字段treeFormat(TREE_FORMAT)广播,并经由 shared-tree 下的字段与迁移逻辑(fields.ts、migration.ts)维护。
房间的本地持久化与重连
房间在本地持久化——刷新页面后你会自动以相同状态重新加入:刷新时路由/share/<room-id>会重建会话,先读取 IndexedDB 中的本地副本,再通过信令重新发现并连接其他对等端。等待期间,会话以ROOM_STATUS_TICK_MS = 500毫秒的间隔轮询信令连接状态,超过ROOM_UNREACHABLE_MS = 20_000毫秒仍无法触达即判定房间不可达(constants.ts、session.ts)。
实用提示
- 浏览器与桌面端均可用:协作功能同时支持 Web 版与桌面版,桌面版通过
openpencil://join链接从浏览器无缝接管房间; - 房间 ID 加密学随机:只有拿到链接的人能加入,无需密码即具备访问门槛;
- 过期光标自动清理:有人断开连接时,其陈旧光标会被自动清除。
相关阅读
- 官方协作文档:packages/docs/programmable/collaboration.md
- 协作源码:src/app/collab(含 trystero.ts、session.ts、yjs-sync.ts、shared-tree)
- 协作测试:tests/app/collab(concurrent-edits.test.ts、random-edits.test.ts、layer-tree.test.ts)
- 其他可编程接口文档:packages/docs/programmable
- 前端
- 桌面应用
- AI 应用
- MCP 服务
【免费下载链接】open-pencil
AI-native design editor. Open-source Figma alternative.
相关推荐
OpenPencil 实时协作指南:基于 Yjs CRDT 与 WebRTC 的无服务器 P2P 设计协作
OpenPencil 实时协作指南:基于 Yjs CRDT 与 WebRTC 的无服务器 P2P 设计协作 OpenPencil 内置了基于浏览器到浏览器(P2
前端桌面应用AI 应用MCP 服务OpenPencil 实时协作完全指南:基于 WebRTC 与 Yjs 的无服务器 P2P 协同编辑
OpenPencil 实时协作完全指南:基于 WebRTC 与 Yjs 的无服务器 P2P 协同编辑 导读 OpenPencil( 项目仓库 https://l
前端桌面应用AI 应用MCP 服务OpenPencil 实时协作编辑指南:基于 WebRTC P2P、Yjs CRDT 与 IndexedDB 的房间协作机制全解析
OpenPencil 实时协作编辑指南:基于 WebRTC P2P、Yjs CRDT 与 IndexedDB 的房间协作机制全解析 本篇指南以 collabor
前端桌面应用AI 应用MCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考