npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pky/editor

v0.0.3

Published

基于 TipTap + RxJS + BroadcastChannel API 实现的实时跨标签页协作富文本编辑器。

Readme

跨标签页协作编辑器

基于 TipTap + RxJS + BroadcastChannel API 实现的实时跨标签页协作富文本编辑器。

功能特性

  • 自动跨标签页同步:在任意标签页编辑内容,其他标签页实时同步
  • 实时在线状态:显示当前打开的所有标签页及用户信息
  • 光标位置同步:查看其他标签页用户的光标位置
  • 自动用户管理:标签页打开时自动加入,关闭时自动移除
  • 无需后端:纯前端实现,使用 BroadcastChannel API
  • 防抖优化:内容同步和光标位置更新都经过防抖处理

技术架构

核心技术栈

  • TipTap: 富文本编辑器核心
  • RxJS: 响应式编程,处理事件流
  • BroadcastChannel API: 浏览器原生跨标签页通信
  • React: UI 框架

架构设计

┌─────────────────────────────────────────────────────────┐
│                  CollaborationEditor                     │
│  (React Component)                                       │
└──────────────────┬──────────────────────────────────────┘
                   │
         ┌─────────┴─────────┐
         │                   │
    ┌────▼────┐      ┌──────▼──────┐
    │ TipTap  │      │useCollaboration│
    │ Editor  │      │    Hook      │
    └────┬────┘      └──────┬──────┘
         │                   │
         │         ┌─────────▼─────────┐
         └────────►│CollaborationExtension│
                   │   (TipTap Plugin)  │
                   └─────────┬─────────┘
                             │
                   ┌─────────┴─────────┐
                   │                   │
            ┌──────▼──────┐   ┌───────▼────────┐
            │   RxJS      │   │ BroadcastChannel│
            │  Subjects   │   │      API        │
            └─────────────┘   └────────┬────────┘
                                       │
                              ┌────────▼────────┐
                              │  Other Tabs     │
                              │  同步更新        │
                              └─────────────────┘

使用方法

基础用法

import { CollaborationEditor } from '@pky/editor'

function App() {
  return <CollaborationEditor />
}

自定义配置

<CollaborationEditor
  userName="张三"
  userColor="#3b82f6"
  initialContent="<p>自定义初始内容</p>"
/>

Props

| 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | userName | string | 用户{随机数} | 当前用户显示名称 | | userColor | string | 随机颜色 | 用户标识颜色 | | initialContent | string | 默认提示文本 | 编辑器初始内容(HTML) |

工作原理

1. 标签页生命周期

// 标签页打开时
onCreate() {
  - 生成唯一 tabId
  - 创建用户信息(随机名称和颜色)
  - 广播 USER_JOINED 消息
  - 其他标签页收到后回复自己的信息
}

// 标签页关闭时
beforeunload() {
  - 广播 USER_LEFT 消息
  - 其他标签页移除该用户
}

2. 内容同步

// 用户编辑时
onUpdate() {
  - 将内容变更发送到 documentChanges$ Subject
  - 经过 300ms 防抖
  - 广播 CONTENT_CHANGE 消息到其他标签页
}

// 其他标签页接收
onReceive(CONTENT_CHANGE) {
  - 检查不是自己的变更
  - 使用 setContent(content, false) 更新编辑器
  - false 参数防止触发新的 onUpdate 事件
}

3. 光标同步

// 光标移动时
onSelectionUpdate() {
  - 将光标位置发送到 cursorUpdates$ Subject
  - 经过 100ms 防抖
  - 广播 CURSOR_UPDATE 消息
}

// 其他标签页接收
onReceive(CURSOR_UPDATE) {
  - 更新用户列表中该用户的光标位置
  - UI 显示光标图标
}

4. 防抖优化

使用 RxJS 的 debounceTime 操作符:

  • 内容同步: 300ms 防抖,避免每次按键都广播
  • 光标同步: 100ms 防抖,保证流畅度同时减少消息频率

消息类型

type BroadcastMessage =
  | { type: 'USER_JOINED'; payload: User }
  | { type: 'USER_LEFT'; payload: { userId: string } }
  | { type: 'CONTENT_CHANGE'; payload: { content: any; userId: string } }
  | { type: 'CURSOR_UPDATE'; payload: { userId: string; position: { from: number; to: number } } }

数据流

用户输入
  ↓
TipTap onUpdate
  ↓
documentChanges$ Subject
  ↓
debounceTime(300ms)
  ↓
BroadcastChannel.postMessage()
  ↓
其他标签页 BroadcastChannel.onmessage
  ↓
messages$ Subject
  ↓
CollaborationExtension 处理
  ↓
editor.commands.setContent()
  ↓
UI 更新

浏览器兼容性

需要支持 BroadcastChannel API 的浏览器:

  • ✅ Chrome 54+
  • ✅ Firefox 38+
  • ✅ Safari 15.4+
  • ✅ Edge 79+

查看完整兼容性

测试方法

  1. 启动开发服务器:

    npm run dev
  2. 打开浏览器访问 http://localhost:5174

  3. 复制标签页(Cmd/Ctrl + T,输入同样的 URL)

  4. 在任意标签页中编辑内容,观察其他标签页实时同步

  5. 关闭某个标签页,观察用户列表更新

文件结构

packages/editor/src/
├── extensions/
│   └── collaboration.ts      # TipTap 协作扩展
├── hooks/
│   └── use-collaboration.ts  # React Hook
├── utils/
│   ├── broadcast.ts          # BroadcastChannel 封装
│   └── utils.ts              # 工具函数
├── types.ts                  # TypeScript 类型定义
└── index.tsx                 # 主组件

优势与限制

优势

  • 🚀 无需后端,部署简单
  • ⚡ 实时同步,延迟极低
  • 💾 本地化,无需网络请求
  • 🔒 数据安全,不经过服务器

限制

  • ⚠️ 仅支持同一浏览器的多个标签页
  • ⚠️ 不支持跨设备协作
  • ⚠️ 标签页关闭后数据不持久化
  • ⚠️ 需要浏览器支持 BroadcastChannel API

扩展建议

如需跨设备协作,可以考虑:

  1. WebSocket: 实时双向通信
  2. Y.js: 专业的 CRDT 协作框架
  3. Tiptap Collaboration: 官方协作方案(基于 Y.js)
  4. Firebase/Supabase: 实时数据库

License

ISC