import Mention from "@tiptap/extension-mention"; import { mergeAttributes } from "@tiptap/core"; import { ReactNodeViewRenderer } from "@tiptap/react"; import { MentionView } from "./mention-view"; export const BaseMentionExtension = Mention.extend({ addNodeView() { return ReactNodeViewRenderer(MentionView); }, renderHTML({ node, HTMLAttributes }) { const type = node.attrs.type ?? "member"; const prefix = type === "issue" ? "" : "@"; return [ "span", mergeAttributes( { "data-type": "mention" }, this.options.HTMLAttributes, HTMLAttributes, { "data-mention-type": node.attrs.type ?? "member", "data-mention-id": node.attrs.id, }, ), `${prefix}${node.attrs.label ?? node.attrs.id}`, ]; }, addAttributes() { return { ...this.parent?.(), type: { default: "member", parseHTML: (el: HTMLElement) => el.getAttribute("data-mention-type") ?? "member", renderHTML: () => ({}), }, }; }, markdownTokenizer: { name: "mention", level: "inline" as const, start(src: string) { return src.search(/\[@?[^\]]+\]\(mention:\/\//); }, tokenize(src: string) { const match = src.match( /^\[@?([^\]]+)\]\(mention:\/\/(\w+)\/([^)]+)\)/, ); if (!match) return undefined; return { type: "mention", raw: match[0], attributes: { label: match[1], type: match[2] ?? "member", id: match[3] }, }; }, }, parseMarkdown: (token: any, helpers: any) => { return helpers.createNode("mention", token.attributes); }, renderMarkdown: (node: any) => { const { id, label, type = "member" } = node.attrs || {}; const prefix = type === "issue" ? "" : "@"; return `[${prefix}${label ?? id}](mention://${type}/${id})`; }, });