import { Node } from "@tiptap/core"; export interface WikilinkAttrs { pageId: string | null; slugId?: string | null; spaceSlug?: string | null; title: string; alias: string | null; } declare module "@tiptap/core" { interface Commands { wikilink: { insertWikilink: (attrs: WikilinkAttrs) => ReturnType; }; } } /** * Shared Wikilink node (schema only, no NodeView). * * Lives in @docmost/editor-ext so both the Hocuspocus server and the React * client share the exact same schema. The client extends this node to add * the React NodeView, input rule, and suggestion plugin. * * Without registering this node on the server, Hocuspocus' jsonToNode strips * wikilink nodes on save (unknown node type). */ export const WikilinkNode = Node.create({ name: "wikilink", group: "inline", inline: true, atom: true, selectable: true, addAttributes() { return { pageId: { default: null, parseHTML: (el: HTMLElement) => el.getAttribute("data-page-id"), renderHTML: (attrs) => attrs.pageId ? { "data-page-id": attrs.pageId } : {}, }, slugId: { default: null, parseHTML: (el: HTMLElement) => el.getAttribute("data-slug-id"), renderHTML: (attrs) => attrs.slugId ? { "data-slug-id": attrs.slugId } : {}, }, spaceSlug: { default: null, parseHTML: (el: HTMLElement) => el.getAttribute("data-space-slug"), renderHTML: (attrs) => attrs.spaceSlug ? { "data-space-slug": attrs.spaceSlug } : {}, }, title: { default: "", parseHTML: (el: HTMLElement) => el.getAttribute("data-title") ?? el.textContent ?? "", renderHTML: (attrs) => ({ "data-title": attrs.title }), }, alias: { default: null, parseHTML: (el: HTMLElement) => el.getAttribute("data-alias"), renderHTML: (attrs) => attrs.alias ? { "data-alias": attrs.alias } : {}, }, }; }, parseHTML() { return [{ tag: "span[data-wikilink]" }]; }, renderHTML({ HTMLAttributes, node }) { const display = node.attrs.alias ?? node.attrs.title ?? "?"; const isBroken = !node.attrs.pageId; return [ "span", { "data-wikilink": "true", ...HTMLAttributes, class: isBroken ? "wikilink wikilink--broken" : "wikilink", }, `[[${display}]]`, ]; }, addCommands() { return { insertWikilink: (attrs: WikilinkAttrs) => ({ commands }) => commands.insertContent({ type: this.name, attrs }), }; }, }); export default WikilinkNode;