探索 Tiptap V3 的最新功能

自定义建议显示方式

AI 建议扩展是无头且完全可定制的。这意味着你可以完全控制建议在编辑器中的显示方式。

显示加载建议时的加载/错误状态

你可以通过读取扩展的存储对象来访问当前的加载状态

const storage = editor.extensionStorage.aiSuggestion
if (storage.isLoading) {
  // 显示加载动画
} else if (storage.error) {
  // 显示错误信息。
}

storage.error 属性将包含加载建议时抛出的错误对象。你可以根据错误类型使用该对象显示不同的错误信息。

默认建议样式

默认情况下,AI 建议扩展会向每个建议添加 CSS 类 tiptap-ai-suggestion。它还会添加带有以下颜色变量的 style 属性:--tiptap-ai-suggestion-color--tiptap-ai-suggestion-background-color。类 tiptap-ai-suggestion 可用于在编辑器中为建议应用简单样式。

.tiptap-ai-suggestion {
  border-bottom: 2px solid var(--tiptap-ai-suggestion-color);
  margin-bottom: -2px;
}

如果需要更高级的样式,请使用 getCustomSuggestionDecoration 配置选项

选中建议

当光标悬停在建议上时,该建议被视为“选中”。

你可以从扩展的存储对象中读取选中的建议

const storage = editor.extensionStorage.aiSuggestion
const selectedSuggestion = storage.getSelectedSuggestion()

要通过编程方式选中某个建议,请使用 selectAiSuggestion 命令。

editor.commands.selectAiSuggestion(suggestionId)

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

默认情况下,AI 建议扩展会向选中的建议应用 CSS 类 tiptap-ai-suggestion--selected。此类可用于在编辑器中为选中的建议添加样式。

.tiptap-ai-suggestion--selected {
  background-color: var(--tiptap-ai-suggestion-background-color);
  transition: background-color 0.5s;
}

自定义建议的外观

getCustomSuggestionDecoration 选项允许你控制建议的外观,并为用户提供视觉提示。你可以向建议添加自定义 CSS 类,也可以在建议前后添加自定义元素。这对于向建议添加弹出层、工具提示、图标或其他元素非常有用。

自定义建议样式是通过 Prosemirror Decorations API 应用的。

要了解如何在选中建议时显示弹出层,请参考本指南

AiSuggestion.configure({
  getCustomSuggestionDecoration({ suggestion, isSelected, getDefaultDecorations }) {
    // 你可以将 AI 建议扩展的默认装饰与自定义装饰结合起来
    const decorations = getDefaultDecorations()

    // 在建议文本后添加自定义元素
    decorations.push(
      Decoration.widget(suggestion.deleteRange.to, () => {
        const element = document.createElement('span')
        element.textContent = '⚠️'
        return element
      }),
    )
    return decorations
  },
})

选中建议时显示弹出层

现代 AI 建议的一个核心功能是在选中建议时显示弹出层或工具提示。该弹出层通常提供该建议的额外信息,并允许用户接受或拒绝该建议。

要在选中建议时显示弹出层,需使用 getCustomSuggestionDecoration 选项。该函数允许你向建议添加自定义元素,包括弹出层。

下面是用 React UI 库简化实现的示例。

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

AiSuggestion.configure({
  getCustomSuggestionDecoration({ suggestion, isSelected, getDefaultDecorations }) {
    const decorations = getDefaultDecorations()

    // 然后,创建包含该 HTML 元素的 Prosemirror 装饰
    if (isSelected) {
      decorations.push(
        Decoration.widget(suggestion.deleteRange.to, () => {
          const element = document.createElement('span')

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

const selectedSuggestion = editor.extensionStorage.aiSuggestion.getSelectedSuggestion()
if (popoverElement && selectedSuggestion) {
  // 然后,将内容添加到该自定义元素中。在此示例中,我们使用 React 门户将弹出层渲染到编辑器内。
  ReactDOM.createPortal(<Popover suggestion={selectedSuggestion} />, popoverElement)
}

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

在弹出层中渲染建议时,你可能希望显示该句子的前后单词,为用户提供更多上下文。我们已经创建了 getNextWordgetPreviousWord 实用函数,免去你自己实现的麻烦。你可以从 @tiptap-pro/extension-ai-suggestion 库中导入它们。

import { getNextWord, getPreviousWord } from '@tiptap-pro/extension-ai-suggestion'

// 获取句子中的前一个单词。
const { previousWord } = getPreviousWord(editor, suggestion.deleteRange.from)
// 获取句子中的下一个单词及其后的标点符号(如果已到句尾)。
const { nextWord, punctuationMark } = getNextWord(editor, suggestion.deleteRange.to)

在编辑器外的侧边栏显示建议

你可以从扩展的存储对象访问当前建议。

const storage = editor.extensionStorage.aiSuggestion
const suggestions = storage.getSuggestions()

然后,你可以使用这些数据在 UI 中(编辑器外)渲染建议。以下是使用 React UI 库实现的示例:

// 从编辑器状态中获取建议。
const storage = editor.extensionStorage.aiSuggestion
const suggestions = storage.getSuggestions()

// 在 UI 中渲染建议
return (
  <div>
    {suggestions.map((suggestion) => (
      <div key={suggestion.id}>
        <p>{suggestion.deleteText}</p>
        <ul>
          {suggestion.replacementOptions.map((option) => (
            <li key={option.id}>{option.addText}</li>
          ))}
        </ul>
      </div>
    ))}
  </div>
)