类型
该扩展导出了用于处理建议的 TypeScript 类型。
SuggestionType
查询 API 返回的公开建议类型:
type SuggestionType = 'add' | 'delete' | 'replace' | 'markChange' | 'sink' | 'lift' | 'split'sink和lift是待处理的列表项嵌套更改(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 的新增内容中):
| 建议 | text | fullText |
|---|---|---|
| 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包含旧的(删除的)文本deletionRange和insertionRange给出每个部分的精确位置from和to覆盖两部分的完整范围
对于 markChange 建议:
text包含受影响的文本内容markChanges包含跟踪的标记操作,包括标记名称和属性
对于块级节点建议(例如插入或删除一个段落或标题):
text始终包含受影响节点的实际文本内容——绝不使用[nodeName]占位符insertedNodes包含每个插入节点的完整 JSON 表示deletedNodes包含每个删除节点的完整 JSON 表示
在构建更丰富的 UI 时,请使用 insertedNodes 和 deletedNodes 来检查节点类型和属性,例如在建议文本旁显示节点类型徽标。
对于 split 建议:
text始终为空,因为块级拆分既不会添加文本,也不会删除文本from和to覆盖第二个块,即拆分后创建的块
请为拆分建议自行渲染标签(例如“段落分隔”),而不要使用 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 = () => HTMLElementTrackedChangesOptions
扩展的配置选项:
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'