基于 Tiptap + Yjs + Hocuspocus 的富文本协同项目,期待你的参与 😍😍😍

在线预览地址

Github 地址

如果你对这个项目感兴趣,或者想参与贡献的,可以看 issue 认领需求,如果想加入交流群可以添加我微信 yunmz777 拉你进群。

技术选型

框架

技术 用途 官网链接
Next.js React 全栈框架,支持 SSR、App Router 等特性 nextjs.org
React 19 UI 框架,构建组件化用户界面 react.dev
TypeScript 静态类型系统,提升开发可靠性 www.typescriptlang.org

富文本编辑器体系(Tiptap)

技术 用途 官网链接
Tiptap 基于 ProseMirror 的无头富文本编辑器框架 tiptap.dev
Tiptap Pro 扩展 表格、占位符、数学公式、拖拽、节点 ID、Emoji 等增强功能 Pro 扩展说明
Yjs 数据同步框架,实现多人协同编辑 yjs.dev
@hocuspocus 基于 Yjs 的 WebSocket 协同服务端 hocuspocus.dev

⚙️ 工程化工具链

技术 用途 官网链接
ESLint 代码质量与规范检查工具 eslint.org
Prettier 代码自动格式化 prettier.io
Husky Git 提交钩子,配合 Lint 检查 typicode.github.io/husky
Commitizen + cz-git 规范化 Git 提交信息 commitizen-tools.github.io/commitizen/
Vitest 单元测试框架,Vite 原生支持 vitest.dev
Playwright 端对端浏览器测试 playwright.dev

样式

技术 用途 官网链接
shadcn/ui 基于 Radix UI 封装的现代 React 组件库,支持主题切换、无锁样式、自定义 Tailwind 风格 ui.shadcn.com
Tailwind CSS 原子化 CSS 样式库,与 shadcn 深度集成 tailwindcss.com
Radix UI 无样式的可访问性基础组件,shadcn 的核心依赖 www.radix-ui.com
tailwindcss-animate Tailwind 的动画插件,配合 @keyframes 使用 github.com/jamiebuilds...

其他精选依赖

技术 用途 官网链接
Zustand 轻量级状态管理库,支持持久化、异步逻辑 zustand.pmnd.rs
Framer Motion 高性能动画库,适用于组件过渡、交互反馈等 www.framer.com/motion
Lucide 现代图标库,支持 React 组件直接引入 lucide.dev
Date-fns 常用日期处理函数库,API 简洁,体积小 date-fns.org

目录结构

txt 复制代码
src/
├── app/                # Next.js 应用目录,包含页面路由、布局配置等
├── components/         # 可复用的 UI 组件库
├── extensions/         # Tiptap 编辑器的自定义扩展功能
├── hooks/              # 自定义 React Hooks
├── services/           # 业务逻辑服务层(如 API 调用、请求封装等)
├── stores/             # 状态管理(如使用 Zustand、Jotai 等)
├── styles/             # 全局样式和样式模块
├── types/              # 全局 TypeScript 类型定义
├── utils/              # 工具函数、辅助方法
├── worker/             # Web Worker 实现,用于异步或性能密集型任务
└── middleware.ts       # Next.js 中间件,用于请求拦截、认证控制等

其中 styles 里面包含了大量的 tiptap 的 css 样式,这些 CSS 文件用于为 Tiptap 提供完整的富文本样式支持。由于 Tiptap 是无头组件,所有样式(如段落、代码块、表格、列表、协同编辑等)都需自行定义。每个 CSS 文件针对一个功能模块进行样式分离,便于维护与扩展。这种拆分方式能保持样式结构清晰、职责明确。

每个不同的文件都代表不同的插件的样式,例如 code.css 代表代码块 <pre><code> 的样式定义,包括背景色、字体、行号样式,适配 CodeBlock 插件(如支持语法高亮的效果)。

对于 components 又分为两个不同的分类,src 目录下的是全局公共组件或者业务性质可共用的,在 app 目录下的是每个页面或者一个 layout 路由组下的业务组件,为了避免渲染成路由,可用 _components 命名的方式命名。

核心模块

Service 封装和调用

service 目录下的 request.ts 为全局的 fetch 封装:

这段代码封装了一个基于 fetch 的请求工具类,支持统一拦截、超时控制、错误处理与自动重试等高级功能。同时,它提供了 get/post/put/delete/patch 等方法,返回统一格式的响应结果,便于在项目中稳定复用。

对于不同模块的,可以再 service 目录下再新建一个新的目录作为特定模块的封装,例如我有一个 user 模块,index.ts 作为函数的封装,type.ts 作为接口的出参和入参的类型。

具体调用时可以不用添加 try...catch 语法捕捉,可以在具体调用时传入不同的错误结果不同的处理,由统一的 fetch 实例来处理。

tiptap 扩展

借助 tiptap 的强大的扩展功能,我们可以在原来的基础上扩展任务我们想要的功能,甚至你可以在 tiptap 上扩展一个页面。

如果要创建一个扩展,一般遵循以下原则:

ts 复制代码
import { Node, NodeViewRendererProps } from "@tiptap/core";
// - Node: 创建扩展的核心 API
// - NodeViewRendererProps: 自定义组件的 props 类型(含 editor、node 等)

import {
  NodeViewWrapper, // 将 React 组件包装为 NodeView
  ReactNodeViewRenderer, // 用于包裹 React 节点,Tiptap 会识别它作为 NodeView 的容器
  useEditorState, // 用于订阅 editor 的状态(如目录、选中状态等)
} from "@tiptap/react";

// 👉 你的自定义组件(实际渲染逻辑)
import MyReactComponent from "./MyReactComponent";

// 👉 正式定义扩展
export const MyNode = Node.create({
  name: "myNode", // 节点名称,必须唯一
  group: "block", // 节点分组,可选 block / inline / list
  atom: true, // 原子节点,不可编辑内部内容
  draggable: true, // 是否允许拖拽移动
  inline: false, // 是否是 inline 类型,默认为 block

  // HTML -> Node 映射(反序列化)
  parseHTML() {
    return [
      {
        tag: 'div[data-type="my-node"]',
      },
    ];
  },

  // Node -> HTML 映射(序列化)
  renderHTML({ HTMLAttributes }) {
    return ["div", { ...HTMLAttributes, "data-type": "my-node" }, ""]; // SSR 输出结构
  },

  // 客户端渲染视图(NodeView)------ 仅在浏览器中执行
  addNodeView() {
    if (typeof window !== "undefined") {
      return ReactNodeViewRenderer(MyReactComponent);
    }
    return null; // SSR 时跳过 NodeView
  },

  // 自定义命令:插入该节点
  addCommands() {
    return {
      insertMyNode:
        () =>
        ({ commands }) => {
          return commands.insertContent({
            type: this.name,
          });
        },
    };
  },
});

创建完成之后可以在这里添加并导出:

这两个文件都要。

协同

贡献指南

1. Fork 仓库

首先,fork 仓库到你的 GitHub 账户中。这会创建一个你自己的仓库副本。

2. 克隆仓库

在你的本地机器上克隆你刚刚 fork 的仓库:

bash 复制代码
git clone https://github.com/你的用户名/项目名.git
cd 项目名

3. 添加上游远程仓库

为了保持你的仓库与原始仓库同步,请添加上游远程仓库:

bash 复制代码
git remote add upstream https://github.com/xun082/DocFlow.git

4. 创建新分支

在开始工作之前,请确保你创建了一个新的分支:

bash 复制代码
git checkout -b feat/你的分支名

其中 feat/ 是表示"功能开发分支"的前缀,后者为你具体的功能点描述,例如:

bash 复制代码
feat/login-page             # 开发登录页面功能
feat/user-permission        # 实现用户权限控制
feat/invite-code-refactor   # 重构邀请码功能模块

对于不同的功能有不同的前缀:

前缀 说明
feature/ 新功能开发
fix/ 缺陷修复
hotfix/ 线上紧急修复
refactor/ 重构代码,不涉及功能变更
test/ 添加或修改测试相关内容
chore/ 构建、依赖等杂项维护
docs/ 文档更新

开发流程

在开启之前,请确保你的 NodeJs 版本大于或者等于 20,PNPM 版本大于或者等于 9

1. 安装依赖

在你开始开发之前,请安装所有的依赖:

bash 复制代码
pnpm install

2. 运行项目

为了确保你在一个正常运行的环境下进行开发,启动项目:

bash 复制代码
pnpm dev

3. 提交更改

在提交你的更改之前,请确保你进行了适当的代码格式化和 lint:

bash 复制代码
pnpm lint
pnpm format

然后提交你的更改:

bash 复制代码
git add .
pnpm commit

5. 同步你的分支

在你准备好提交你的更改之前,请确保你的分支是最新的:

bash 复制代码
git fetch upstream
git rebase upstream/main

6. 推送分支

将你的分支推送到你自己的仓库:

bash 复制代码
git push origin feature/你的分支名

7. 创建 Pull Request

在 GitHub 上,导航到你 fork 的仓库,点击 "Compare & pull request" 按钮。请确保你详细描述了你所做的更改。

代码审查

所有的 Pull Request 都会被审查。请注意以下几点:

  • 你的代码是否清晰且易于理解。
  • 你是否遵循了项目的代码风格和规范。
  • 你是否添加了适当的测试。
  • 你的更改是否与现有的代码兼容。

常见问题

如何报告 Bug?

如果你发现了 Bug,请在 GitHub 上创建一个 Issue,并尽可能详细地描述 Bug 及其复现步骤。

如何请求新功能?

如果你有新功能的建议,请在 GitHub 上创建一个 Issue,详细描述你的建议及其潜在的用途。

相关推荐
JSON_L2 小时前
Vue rem回顾
前端·javascript·vue.js
GISer_Jing3 小时前
JavaScript 中Object、Array 和 String的常用方法
开发语言·javascript·ecmascript
brzhang3 小时前
颠覆你对代码的认知:当程序和数据只剩下一棵树,能读懂这篇文章的人估计全球也不到 100 个人
前端·后端·架构
斟的是酒中桃4 小时前
基于Transformer的智能对话系统:FastAPI后端与Streamlit前端实现
前端·transformer·fastapi
烛阴4 小时前
Fract - Grid
前端·webgl
JiaLin_Denny4 小时前
React 实现人员列表多选、全选与取消全选功能
前端·react.js·人员列表选择·人员选择·人员多选全选·通讯录人员选择
brzhang4 小时前
我见过了太多做智能音箱做成智障音箱的例子了,今天我就来说说如何做意图识别
前端·后端·架构
为什么名字不能重复呢?5 小时前
Day1||Vue指令学习
前端·vue.js·学习
eternalless5 小时前
【原创】中后台前端架构思路 - 组件库(1)
前端·react.js·架构