探索 Tiptap V3 的最新功能

自定义变更的显示方式

AI Changes 扩展旨在提供灵活性。作为一个无 UI 库,它让你可以完全控制编辑器中变更的显示方式。

默认样式

默认情况下,AI Changes 扩展会应用 CSS 类以突出显示修改过的内容:

  • tiptap-ai-changes--old 表示删除的文本
  • tiptap-ai-changes--new 表示插入的文本

该扩展不包含任何内置样式,因此你需要自行定义 CSS。以下是用于变更高亮的基础样式完整示例:

:root {
  --color-green-100: oklch(0.962 0.044 156.743);
  --color-green-700: oklch(0.527 0.154 150.069);
  --color-red-100: oklch(0.936 0.032 17.717);
  --color-red-700: oklch(0.505 0.213 27.518);
}

.tiptap-ai-changes--old,
.tiptap-ai-changes--old > * {
  color: var(--color-red-700);
  background-color: var(--color-red-100);
}

.tiptap-ai-changes--new,
.tiptap-ai-changes--new > * {
  color: var(--color-green-700);
  background-color: var(--color-green-100);
}

这会给删除文本应用红色背景,给插入文本应用绿色背景。

想要更高级的样式,请使用 getCustomDecorations 配置选项

选中的变更

当选择光标在某个变更上时,该变更被视为“选中”。

你可以从扩展的存储对象中获取当前选中的变更:

const storage = editor.extensionStorage.aiChanges
const selectedChange = storage.getSelectedChange()

要以编程方式选中某个变更,可以使用 selectAiChange 命令。

editor.commands.selectAiChange(changeId)

这会将光标移到变更的起始位置,从而使其被视为“选中”。

选中变更的文本可以在 getCustomDecorations 函数中引用,以应用自定义样式。

自定义变更外观

getCustomDecorations 选项允许你控制变更的外观,并向用户提供视觉提示。

它接受以下参数:

  • change:包含变更信息的变更对象。
  • changes:所有追踪变更的列表。
  • isSelected:布尔值,表示该变更是否被选中。当光标在变更上时,视为选中。
  • getDefaultDecorations:返回该变更默认装饰的函数。如果不提供 getCustomDecorations 函数,则使用默认装饰。
  • editor:Tiptap 编辑器实例。
  • previousDoc:AI 修改前的文档。通过将当前文档与此文档对比,得出变更。
  • currentDoc:AI 修改后的文档。
AiChanges.configure({
  getCustomDecorations({ change, isSelected, getDefaultDecorations }) {
    // 你可以将 AI Changes 扩展的默认装饰与自定义装饰结合使用
    const decorations = getDefaultDecorations()

    // 在变更插入文本之后添加自定义元素
    decorations.push(
      Decoration.widget(change.newRange.to, () => {
        const element = document.createElement('span')

        element.textContent = '✅'
        return element
      }),
    )
    return decorations
  },
})

自定义样式和元素通过 Prosemirror Decorations API 实现。

要了解如何在选中变更时显示弹出层,请查看 此指南

选中变更时显示弹出层

在大多数用户审核流程中,你需要在选中的变更上显示弹出层或工具提示,提供接受或拒绝操作。

要在选中变更时显示弹出层,请使用 getCustomDecorations 选项。它允许你向变更添加自定义元素,包括弹出层。

以下是使用 React 的简化示例:

// 首先,定义一个 Hook 用于保存将渲染弹出层的 HTML 元素
const [popoverElement, setPopoverElement] = useState<HTMLElement | null>(null)

AiChanges.configure({
  getCustomDecorations({ change, isSelected, getDefaultDecorations }) {
    const decorations = getDefaultDecorations()

    // 选中变更时,在插入文本之后创建一个 Prosemirror 装饰,包含 HTML 元素
    if (isSelected) {
      decorations.push(
        Decoration.widget(change.newRange.to, () => {
          const element = document.createElement('span')

          setPopoverElement(element)
          return element
        }),
      )
    }
    return decorations
  },
})

const selectedChange = editor.extensionStorage.aiChanges.getSelectedChange()
if (popoverElement && selectedChange) {
  // 然后,将 HTML 内容添加到自定义元素中。本例中使用 React Portals 渲染弹出层。
  ReactDOM.createPortal(<Popover change={selectedChange} />, popoverElement)
}

我们推荐使用 Floating UI 库来显示弹出层。你可以在 示例演示 中查看如何实现。

在编辑器外侧边栏显示变更

你可以从扩展的存储对象访问变更数据:

const storage = editor.extensionStorage.aiChanges
const changes = storage.getChanges()

然后用这些数据渲染自定义 UI 组件。下面是一个使用 React 的示例:

// 从编辑器状态获取变更
const storage = editor.extensionStorage.aichange
const changes = storage.getchanges()

// 在 UI 中渲染变更
return (
  <div>
    {changes.map((change) => (
      <div key={change.id}>
        <button onClick={() => editor.commands.acceptAiChange(change.id)}>接受</button>
        <button onClick={() => editor.commands.rejectAiChange(change.id)}>拒绝</button>
      </div>
    ))}
  </div>
)

隐藏和显示变更

在某些情况下,你可能想继续追踪变更,但在 UI 中隐藏它们。例如,当你正在流式生成 AI 内容时。你可以使用 setShowAiChanges 命令以编程方式隐藏和显示变更。

// 隐藏变更
editor.commands.setShowAiChanges(false)

// 显示变更
editor.commands.setShowAiChanges(true)

这仅影响变更的视觉显示 —— 追踪机制仍在后台工作,且所有变更仍可通过扩展的存储方法访问。