插入内容工作流程

构建一个在您的 Tiptap 文档中插入或替换内容的工作流程。

查看 GitHub 上的源代码

技术栈

项目概述

该演示使用 AI 工具包的插入内容工作流程,在实时状态下用 AI 生成的内容替换所选内容。

安装

创建一个 Next.js 项目:

npx create-next-app@latest insert-content-workflow

安装核心 Tiptap 包和用于 OpenAI 的 Vercel AI SDK

npm install @tiptap/react @tiptap/starter-kit ai @ai-sdk/react @ai-sdk/openai

安装 Tiptap AI 工具包:

专业版套餐

AI 工具包是一个专业版套餐。安装前,请按照私有注册表指南设置对私有 NPM 注册表的访问权限。

npm install @tiptap-pro/ai-toolkit @tiptap-pro/ai-toolkit-tool-definitions

服务器设置

创建一个 API 端点,使用 Vercel AI SDK 调用 OpenAI 模型。

如果您的后端不是 TypeScript,参见非 TypeScript 后端

在该 API 端点内,使用 createInsertContentWorkflow 函数创建并配置插入内容工作流程。该流程包含一个可直接使用的系统提示,用于指导 AI 模型如何生成内容。

用户消息应为包含以下属性的 JSON 对象:

  • task:要完成的任务
  • replace:被替换的 HTML 内容(可选)
  • before:之前的 HTML 内容(可选)
  • after:之后的 HTML 内容(可选)

当 AI 模型生成响应时,API 端点将 HTML 内容以流的形式发送到客户端。

// app/api/insert-content-workflow/route.ts
import { openai } from '@ai-sdk/openai'
import { createInsertContentWorkflow } from '@tiptap-pro/ai-toolkit-tool-definitions'
import { streamText } from 'ai'

export async function POST(req: Request) {
  const { task, replace } = await req.json()

  // 创建并配置插入内容工作流程(使用默认设置)。
  // 它包含可直接使用的系统提示。
  const workflow = createInsertContentWorkflow()

  const result = streamText({
    model: openai('gpt-5.4-mini'),
    // 系统提示
    system: workflow.systemPrompt,
    // 用户消息,包含任务和要替换的内容,格式为 JSON 对象。
    prompt: JSON.stringify({
      task,
      replace,
    }),
  })

  return result.toTextStreamResponse()
}

客户端设置

创建一个 React 组件,渲染编辑器并将 AI 生成的内容流入选区。

首先,当工作流程启动时,使用 AI 工具包的 getHtmlRange 方法获取当前选区。然后调用 API 端点启动工作流程。API 端点会返回一个 HTML 流,可以通过 AI 工具包的 streamHtml 方法插入到编辑器中。

// app/insert-content-workflow/page.tsx
'use client'

import { EditorContent, useEditor, useEditorState } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { AiToolkit, getAiToolkit } from '@tiptap-pro/ai-toolkit'
import { useState } from 'react'
import { Selection } from '@tiptap/extensions'

export default function Page() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [StarterKit, AiToolkit, Selection],
    content: `<p>选中文本后点击“添加表情”按钮,为选区添加表情符号。</p>`,
  })

  // 当 AI 生成内容时显示加载状态
  const [isLoading, setIsLoading] = useState(false)

  // 当选区为空时禁用按钮
  const selectionIsEmpty = useEditorState({
    editor,
    selector: (snapshot) => snapshot.editor?.state.selection.empty ?? true,
  })

  if (!editor) return null

  const editSelection = async (task: string) => {
    editor.commands.blur()
    setIsLoading(true)

    const toolkit = getAiToolkit(editor)

    // 使用 AI 工具包以 HTML 格式获取选区
    const selection = toolkit.getHtmlRange(editor.state.selection)
    const selectionPosition = editor.state.selection

    // 调用 API 端点获取编辑后的 HTML 内容
    const response = await fetch('/api/insert-content-workflow', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        task,
        replace: selection,
      }),
    })

    if (!response.ok) {
      throw new Error(`HTTP 错误!状态码:${response.status}`)
    }

    // 响应为 HTML 内容流
    const readableStream = response.body
    if (!readableStream) {
      throw new Error('无响应主体')
    }

    // 使用 AI 工具包将 HTML 流插入选区
    await toolkit.streamHtml(readableStream, {
      position: selectionPosition,
      // 在流处理过程中更新选区,确保选区持续覆盖生成的内容
      onChunkInserted(event) {
        editor.commands.setTextSelection(event.range)
      },
    })

    setIsLoading(false)
  }

  const disabled = selectionIsEmpty || isLoading

  return (
    <div>
      <EditorContent editor={editor} />
      <button onClick={() => editSelection('为这段文字添加表情')} disabled={disabled}>
        {isLoading ? '加载中...' : '添加表情'}
      </button>
    </div>
  )
}

最终效果

通过额外的 CSS 样式,效果是一个打磨完善的应用,能够实时插入或替换成 AI 生成的内容:

查看 GitHub 上的源代码

显示 AI 输入光标

您可以添加 AiCaret 扩展来显示一个光标,指示 AI 正在插入内容的位置。这为用户在内容流式传输期间提供实时的视觉反馈。

import { AiCaret, AiToolkit, getAiToolkit } from '@tiptap-pro/ai-toolkit'

const editor = useEditor({
  extensions: [StarterKit, AiToolkit, AiCaret],
})

请参阅 AI 光标指南 了解配置选项和 CSS 样式。

禁用流式传输

如果您想在不进行流式传输的情况下将内容插入文档,请使用 insertHtml 方法。该方法与 streamHtml 具有相同的选项,只是它会以完整的 HTML 内容作为参数一次性调用。请参阅 API 参考

显示审阅界面

在审阅界面中显示更改,以便用户接受或拒绝它们。

有两种实现方式:

  • 使用 Tracked Changes 扩展来渲染审阅界面。更改会作为文档的一部分保留,并对其他用户可见。
  • AI Toolkit 建议:一种基于装饰的界面,具有临时性,仅对当前文档用户可见。

通过设置 reviewOptions 参数来配置审阅界面。更改将显示为建议,用户可以接受或拒绝。

await toolkit.streamHtml(readableStream, {
  // ... 其他选项

  reviewOptions: {
    // 使用 Tracked Changes 显示审阅界面
    mode: 'trackedChanges',

    // 在将更改插入文档之前预览更改,
    // 使用 AI Toolkit 建议
    mode: 'preview',

    // 使用 AI Toolkit 建议显示审阅界面
    mode: 'review',
  },
})

请参阅 审阅更改 指南了解有关审阅界面的更多信息。

查看 AI Toolkit 演示 了解如何在审阅界面中使用 AI Toolkit 工作流程的示例。

下一步

  • createInsertContentWorkflow 函数的 API 参考
  • getHtmlRange 方法的 API 参考
  • streamHtml 方法的 API 参考
  • 建议:了解如何审阅文档并在编辑器中显示建议。
  • AI 光标:显示 AI 正在插入内容的位置的光标。