當 AI 技術剛剛興起時,我加入了一家 AI 初創公司。由於我們在專案中有著對高級編輯功能的需求,我第一次接觸到了 Tiptap。讓我印象深刻的是,Tiptap 與其他編輯器相比,最大亮點在於它的靈活性和可擴展性。這種高度的定制能力,讓我們能夠根據需求隨時調整和擴展編輯器的功能,完全滿足了我們的獨特要求。
經過一段時間的研究,我漸漸認為 Tiptap 應該是我未來選擇的首選編輯器。另外現在公司很久就不幹了,專案也沒有上線,我完全可以複刻裡面的核心功能。
現在在 dashboard 頁面上能看到的基本就是將來要實現的核心功能了。
首先先貼相關地址:
微信聯繫方式或加群:yunmz777
DocFlow 是一個基於 Tiptap 和 Yjs 構建的智能協作寫作平台,類似飛書的文件功能,支持多人即時協作編輯。平台結合了 AI 生成內容和 RAG 知識庫搜索,幫助用戶在創作過程中快速獲取相關信息,並提供智能寫作建議。
DocFlow 還將具備類似 Dify 的工作流編排功能,用戶可以創建自動化流程,定義不同任務和操作的執行順序,实现任務的不同輸入輸出。透過這個功能,團隊可以高效地管理創作過程,構建智能代理(Agent)來自動處理內容生成、文檔整理等任務,極大提升生產力。
無論是文檔寫作、知識管理,還是複雜流程的自動化,DocFlow 都能提供強大的支持,幫助團隊優化工作流並提升創作效率。
專案採用的 NextJs 和 NestJs 的全端架構,
首選是前端的技術棧核心主要有以下幾個:
未來做流程編排的話會使用 React Flow。
後端核心技術:
目錄結構採用比較常見的結構:
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
目錄下,組件被分為兩個不同的分類:
_components
。service 目錄下的 request.ts
為全局的 fetch
封裝:
這段代碼封裝了一個基於 fetch
的請求工具類,支持統一的請求攔截、超時控制、錯誤處理和自動重試等高級功能。它提供了 get
、post
、put
、delete
、patch
等 HTTP 請求方法,並返回統一格式的響應結果,方便在專案中穩定重用。
對於不同模組的請求,可以在 services 目錄下創建新的子目錄進行模組化封裝。例如,對於 user 模組,可以在 user
目錄下創建 index.ts
來封裝函數邏輯,type.ts
用於定義介面的入參和出參型別。透過這種方式,不同模組的請求邏輯清晰分離,便於維護和擴展。
在實際調用時,無需顯式使用 try...catch
來捕獲錯誤。可以透過傳入不同的錯誤處理邏輯,統一的 fetch
實例會自動處理錯誤並執行相應的處理方式。
借助 Tiptap 強大的擴展功能,我們可以在原有基礎上輕鬆添加所需的功能,甚至可以在 Tiptap 上擴展整個頁面。
創建擴展時,一般遵循以下原則:
import { Node } from "@tiptap/core";
import {
NodeViewWrapper, // 用於包裝 React 組件為 NodeView
ReactNodeViewRenderer, // 用於包裹 React 節點,Tiptap 會識別它作為 NodeView 的容器
} 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"]', // 將 HTML 標籤映射到該節點
},
];
},
// Node -> HTML 映射(序列化)
renderHTML({ HTMLAttributes }) {
return [
"div",
{ ...HTMLAttributes, "data-type": "my-node" }, // 使用自定義數據屬性
"", // 空的內容,原子節點不包含可編輯內容
];
},
// 客戶端渲染視圖(NodeView)—— 僅在瀏覽器中執行
addNodeView() {
// 僅在客戶端執行 NodeView 渲染
if (typeof window !== "undefined") {
return ReactNodeViewRenderer(MyReactComponent);
}
return null; // SSR 時跳過 NodeView
},
// 自定義命令:插入該節點
addCommands() {
return {
insertMyNode: () => ({ commands }) => {
return commands.insertContent({
type: this.name,
});
},
};
},
});
創建完成之後可以在這裡添加並導出:
這兩個文件都要。
Prisma 非常好用,懂的都懂,專案目前 Mysql 的架構如下圖所示:
目前專案中的 AI 播客生成採用消息隊列機制進行處理,主要是因為 TTS 接口存在較高的請求併發。透過使用消息隊列,我們確保每次只處理一個請求,從而避免了併發請求對系統性能的影響。
協同編輯我們可以在文檔這裡點擊文件進行分享,並且可以設置文件的權限以及是否需要密碼等:
之後可以將這個 URL 分享給其他用戶,點擊之後會有這樣的效果:
這個功能跟百度網盤之類的分享類似,這裡的主要使用了 RBAC 和 ACL 進行權限控制。
之後我們就可以進行協同編輯了:
AI 續寫和 AI 問答的功能正在逐步實現,目前已經支持 RAG 了,先看效果:
上面的是續寫的,接下來展示 AI 問答的,首先我們可以上傳我們自己的一些內容:
之後我們執行 AI 問答:
你可以看到我們添加的內容被我們輸出到了這裡了,這個就是我們當前的 RAG 的功能,當然後續會繼續增强:
他的原理如下流程圖所示:
從文檔處理角度來看,實現流程如下:
AI 播客處理流程是用戶上傳簡歷和選擇相對應的面試官,但是最好是相同簡歷對應面試官類型,不然可能生成出來的內容亂七八糟的:
關於編輯器裡面還有很多內容沒有介紹到,更多的可自行體驗:
後續會繼續增強,添加導入導出等功能。
將來要做的內容非常核心,例如側邊欄這邊的:
除了前面講了這些 Agent 和流程編排,將來還會實現如下核心功能:
如果你對 AI 應用與富文本編輯器生態感興趣,歡迎為本倉庫點亮 Star 並開啟 Watch,獲取最新迭代。
希望了解更多服務(企業諮詢、插件定製、參與開發、私有化部署等),請添加微信 yunmz777
(備註:DocFlow)。我將邀請你進入技術交流群,與一線開發者交流實踐經驗、獲取路線圖。