Skip to content

imRichContentHtml

IM 富文本 wire format(HTML 片段)的解析、序列化、消毒、@ 降级与待上传内联图替换。

Wire format 标记

语义HTML 形态
@ 提及<span data-cw-mention data-user-id="…" data-label="…">@昵称</span>
内联图<img data-cw-inline-image src="https:…|data:image/…" width height>
文件<span data-cw-file data-path="…" data-label="…">…</span>
表情<span data-emoji="😀"><img data-emoji="😀" …></span>

常量:IM_RICH_MENTION_ATTRIM_RICH_USER_ID_ATTRIM_RICH_LABEL_ATTRIM_RICH_INLINE_IMAGE_ATTRIM_RICH_FILE_ATTRIM_RICH_FILE_PATH_ATTRIM_RICH_EMOJI_ATTR 等。

函数签名

typescript
type ImRichContentSegment =
  | { type: 'text'; text: string }
  | { type: 'mention'; userId: string; label: string }
  | { type: 'file'; path: string; label: string }
  | { type: 'image'; url: string; width?: number; height?: number; alt?: string }
  | { type: 'emoji'; glyph: string; src?: string }

type MaterializeImRichHtmlOptions = {
  resolveMentionLabel?: (userId: string) => string
  mentionFallbackLabel?: (userId: string) => string
  formatImage?: (url: string) => string
  formatFile?: (path: string, label: string) => string
}

type DemoteOutOfScopeMentionsOptions = {
  allowedUserIds: ReadonlySet<string> | readonly string[]
  resolveMentionLabel?: (userId: string) => string
}

function isImRichHtmlContent(content: string): boolean
function hasImRichInlineClipboardMarkers(html: string): boolean
function contentHasPendingInlineImages(html: string): boolean
function parseImRichContentHtml(content: string): ImRichContentSegment[]
function extractImRichMentionUserIds(content: string): string[]
function materializeImRichHtmlToPlainText(content: string, options?: MaterializeImRichHtmlOptions): string
function serializeImRichContentSegments(segments: readonly ImRichContentSegment[]): string
function demoteOutOfScopeMentionsInImRichHtml(content: string, options: DemoteOutOfScopeMentionsOptions): string
function sanitizeImRichContentHtml(html: string): string
function normalizeImRichEditorClipboardHtml(html: string): string
function resolveImMentionDisplayLabel(userId: string, embeddedLabel?: string, resolvedLabel?: string): string
function replacePendingInlineImageSrcInHtml(
  html: string,
  upload: (dataUrl: string, alt: string) => Promise<string>,
): Promise<string>

前置依赖

replacePendingInlineImageSrcInHtml 需注入上传回调:

参数名类型说明
upload(dataUrl, alt) => Promise<string>data:image/* 上传为 HTTPS URL

MaterializeImRichHtmlOptions / DemoteOutOfScopeMentionsOptions 为可选回调,无全局环境依赖。

主要函数参数

函数关键参数返回值
parseImRichContentHtmlcontent wire HTMLImRichContentSegment[]
serializeImRichContentSegmentssegmentswire HTML 字符串
materializeImRichHtmlToPlainTextcontent, options?通知/引用用纯文本
demoteOutOfScopeMentionsInImRichHtmlcontent, allowedUserIds非成员 @ 降级为 @昵称 文本
sanitizeImRichContentHtmlhtml白名单消毒后的 wire HTML
normalizeImRichEditorClipboardHtml剪贴板 HTML编辑器 wrap → wire 格式
replacePendingInlineImageSrcInHtmlhtml, upload替换 data URL 并 sanitize

工作原理

  1. 识别isImRichHtmlContent 检测是否含 IM 语义 data 属性;hasImRichInlineClipboardMarkers 用于剪贴板 HTML 更严格判定。
  2. 解析parseImRichContentHtml 按 mention / image / file / emoji 正则切分,块间文本经 stripHtmlToText 解码实体。
  3. 序列化serializeImRichContentSegments 转义文本并输出受控 inline 标签;非 https/http/data 图片 URL 跳过。
  4. @ 降级demoteOutOfScopeMentionsInImRichHtml 不在 allowedUserIds 的 mention 改为 text 段 @昵称
  5. 剪贴板normalizeImRichEditorClipboardHtml 将 TipTap NodeView / 气泡 DOM 的 wrap 还原为 wire span/img。
  6. 上传replacePendingInlineImageSrcInHtml 遍历 data: 内联图,调用 upload 后写回 HTTPS 并 sanitize