🎁 100 free AI Toolkit licenses – apply by August 15.Learn more

类型

Paid add-on

该扩展导出了用于处理建议的 TypeScript 类型。

SuggestionType

查询 API 返回的公开建议类型:

type SuggestionType = 'add' | 'delete' | 'replace' | 'markChange' | 'sink' | 'lift' | 'split'
  • sinklift 是待处理的列表项嵌套更改(Tab 和 Shift-Tab)
  • split 是建议的块拆分。接受后会保留两个块,拒绝后会将它们重新合并

SuggestionMarkType

存储在建议标记上的内部标记级别类型。替换建议为其删除和插入部分使用不同的标记类型:

type SuggestionMarkType = 'add' | 'delete' | 'replaceDeletion' | 'replaceInsertion' | 'markChange'

SuggestionNodeType

存储在节点属性中的内部类型。涵盖每一种标记级类型,以及只能存在于节点级别的结构变体:

type SuggestionNodeType = SuggestionMarkType | 'sink' | 'lift' | 'blockSplit'

blockSplit 是公共 split 类型在节点级别的对应类型。它出现在 suggestionType 节点属性和 data-suggestion-type DOM 属性中,因此在 CSS 选择器中使用 blockSplit,而在筛选或根据 suggestion.type 进行切换时使用 split

使用 nodeSuggestionTypeToSuggestionType 进行转换(参见类型辅助工具)。

SuggestionMarkChangeOperation

存储在标记更改建议中的操作:

type SuggestionMarkChangeOperation = 'added' | 'removed'

SuggestionMarkChange

描述存储在 markChange 建议上的一个标记变更:

type SuggestionMarkChange = {
  operation: SuggestionMarkChangeOperation
  markName: string
  markAttrs: Record<string, unknown> | null
}

建议

表示文档中发现的建议:

type Suggestion = {
  id: string
  type: SuggestionType
  userId: string
  createdAt: string
  updatedAt: string
  userMetadata: Record<string, unknown> | null
  from: number
  to: number
  text: string
  fullText: string
  // 仅适用于 'replace' 建议:
  deletionRange?: { from: number; to: number }
  insertionRange?: { from: number; to: number }
  replacedText?: string
  // 仅存在于 'markChange' 建议中:
  markChanges?: SuggestionMarkChange[]
  // 仅在建议影响块级节点时存在:
  insertedNodes?: JSONContent[]
  deletedNodes?: JSONContent[]
}
  • createdAt — 建议首次创建时的 ISO 8601 时间戳
  • updatedAt — 建议最近一次编辑时的 ISO 8601 时间戳。对于早于该字段的旧数据,会回退使用 createdAt

text vs fullText

  • text — 仅由此建议自身撰写的文本,不包括叠加在其上的嵌套(堆叠)建议插入的文本。对于 replace 建议,这里指新的(插入的)文本。
  • fullText — 由此建议的标记所覆盖的完整连续文本,包括来自其他作者的嵌套建议插入的任何文本。对于 replace 建议,这里指新的(插入的)文本。

对于绝大多数建议(没有堆叠),text === fullText。只有当另一位作者通过位于此建议内部的嵌套建议插入文本时,它们才会不同。

请注意,text 只会移除嵌套建议插入的文本。嵌套的删除或标记变更并不会插入内容,因此永远不会减少外层作者的 text。例如,如果作者 A 插入 "hello",而作者 B 在其中删除 "ll",那么 A 的 text 仍然是 "hello"——B 的删除并没有添加任何属于 B 的文本。

示例

作者 A 插入 "Hello world",然后作者 B 在 "world" 前插入 "great "(嵌套在 A 的新增内容中):

建议textfullText
A (add, user-1)"Hello world""Hello great world"
B (add, user-2)"great ""great "
import { findSuggestions } from '@tiptap-pro/extension-tracked-changes'

const suggestions = findSuggestions(editor)
const outer = suggestions.find(s => s.userId === 'user-1')

outer.text // "Hello world"        → 在审阅侧边栏中显示这个
outer.fullText // "Hello great world"  → 当你需要字面范围时使用

在展示“此人修改了什么”时,默认使用 text(审阅面板、评论线程、活动日志)。只有当你需要文档中标记所覆盖的精确连续文本时,才使用 fullText

对于 replace 建议:

  • text 包含新的(插入的)文本
  • replacedText 包含旧的(删除的)文本
  • deletionRangeinsertionRange 给出每个部分的精确位置
  • fromto 覆盖两部分的完整范围

对于 markChange 建议:

  • text 包含受影响的文本内容
  • markChanges 包含跟踪的标记操作,包括标记名称和属性

对于块级节点建议(例如插入或删除一个段落或标题):

  • text 始终包含受影响节点的实际文本内容——绝不使用 [nodeName] 占位符
  • insertedNodes 包含每个插入节点的完整 JSON 表示
  • deletedNodes 包含每个删除节点的完整 JSON 表示

在构建更丰富的 UI 时,请使用 insertedNodesdeletedNodes 来检查节点类型和属性,例如在建议文本旁显示节点类型徽标。

对于 split 建议:

  • text 始终为空,因为块级拆分既不会添加文本,也不会删除文本
  • fromto 覆盖第二个块,即拆分后创建的块

请为拆分建议自行渲染标签(例如“段落分隔”),而不要使用 suggestion.text

SuggestionMarkAttrs

存储在建议标记上的属性:

type SuggestionMarkAttrs = {
  id: string
  type: SuggestionMarkType
  userId: string
  createdAt: string
  updatedAt: string
  userMetadata: Record<string, unknown> | null
  markChanges?: SuggestionMarkChange[] | null
}

SuggestionChangedEventPayload

trackedChanges:suggestionChanged 事件的载荷。每当建议中任何包含内容的字段发生变化时触发,包括范围位置、文本、标记变更或类型。

type SuggestionChangedEventPayload = {
  suggestionId: string
  oldSuggestion: Suggestion
  suggestion: Suggestion
  transaction: Transaction
}
  • oldSuggestion — 变更前建议的快照
  • suggestion — 当前(更新后的)建议
  • transaction — 导致变更的 ProseMirror 事务。

SuggestionRangeChangedEventPayload

trackedChanges:suggestionRangeChanged 事件的载荷。仅在建议的文档位置发生偏移时触发。

type SuggestionRangeChangedEventPayload = {
  suggestionId: string
  oldRange: { from: number; to: number }
  newRange: { from: number; to: number }
  suggestion: Suggestion
  transaction: Transaction
}

BlockSplitMarkerRenderer

构建在块拆分处显示的标记元素。每个标记调用一次,因此每次都必须返回一个新元素:

type BlockSplitMarkerRenderer = () => HTMLElement

TrackedChangesOptions

扩展的配置选项:

type TrackedChangesOptions = {
  enabled: boolean
  userId: string
  userMetadata?: Record<string, unknown> | null
  ignoredTrackingMarks: string[]
  onSuggestionCreate?: (suggestion: Suggestion) => void
  onSuggestionAccept?: (id: string) => void
  onSuggestionReject?: (id: string) => void
  blockSplitMarker?: string | HTMLElement | BlockSplitMarkerRenderer | null
}
  • blockSplitMarker — 在建议的块拆分处渲染的标记。默认为 '¶'。将其设置为 null 可不渲染标记。请参阅 blockSplitMarker

类型辅助工具

该扩展导出了用于处理建议类型的常量和辅助函数:

import {
  SUGGESTION_TYPES,
  isAddLikeType,
  isDeleteLikeType,
  isReplaceMarkType,
  markTypeToSuggestionType,
  nodeSuggestionTypeToSuggestionType,
} from '@tiptap-pro/extension-tracked-changes'

// 常量
SUGGESTION_TYPES.ADD              // 'add'
SUGGESTION_TYPES.DELETE           // 'delete'
SUGGESTION_TYPES.REPLACE_DELETION  // 'replaceDeletion'
SUGGESTION_TYPES.REPLACE_INSERTION // 'replaceInsertion'
SUGGESTION_TYPES.MARK_CHANGE      // 'markChange'
SUGGESTION_TYPES.SINK             // 'sink'
SUGGESTION_TYPES.LIFT             // 'lift'
SUGGESTION_TYPES.BLOCK_SPLIT      // 'blockSplit' (节点级别)
SUGGESTION_TYPES.SPLIT            // 'split' (查询 API)

// 检查标记类型是否表示添加内容(匹配 'add' 和 'replaceInsertion')
isAddLikeType('add') // true
isAddLikeType('replaceInsertion') // true

// 检查标记类型是否表示删除内容(匹配 'delete' 和 'replaceDeletion')
isDeleteLikeType('delete') // true
isDeleteLikeType('replaceDeletion') // true

// 检查标记类型是否为替换子类型之一
isReplaceMarkType('replaceDeletion') // true

// 将标记级别类型转换为公开建议类型
markTypeToSuggestionType('replaceDeletion') // 'replace'
markTypeToSuggestionType('add') // 'add'

// 将节点级别类型转换为面向用户的建议类型
nodeSuggestionTypeToSuggestionType('blockSplit') // 'split'
nodeSuggestionTypeToSuggestionType('replaceInsertion') // 'replace'