diff --git a/packages/tiptap/src/shared/extensions/index.ts b/packages/tiptap/src/shared/extensions/index.ts index bade2ef95f..28e41acd3e 100644 --- a/packages/tiptap/src/shared/extensions/index.ts +++ b/packages/tiptap/src/shared/extensions/index.ts @@ -112,38 +112,61 @@ export const getExtensions = ( Hashtag, Link.extend({ addProseMirrorPlugins() { + const parentPlugins = this.parent?.() || []; return [ + ...parentPlugins, new Plugin({ key: new PluginKey("linkCmdClick"), props: { handleClick(view, pos, event) { - if (!(event.metaKey || event.ctrlKey)) { - return false; - } - const { state } = view; const $pos = state.doc.resolve(pos); const marks = $pos.marks(); const linkMark = marks.find((mark) => mark.type.name === "link"); - - if (linkMark && linkMark.attrs.href) { - event.preventDefault(); - if (options?.onLinkOpen) { - options.onLinkOpen(linkMark.attrs.href); - } else { - window.open( - linkMark.attrs.href, - "_blank", - "noopener,noreferrer", - ); - } + if (!linkMark || !linkMark.attrs.href) { + return false; + } + if (!(event.metaKey || event.ctrlKey)) { return true; } - - return false; + event.preventDefault(); + if (options?.onLinkOpen) { + options.onLinkOpen(linkMark.attrs.href); + } else { + window.open( + linkMark.attrs.href, + "_blank", + "noopener,noreferrer", + ); + } + return true; }, }, }), + new Plugin({ + key: new PluginKey("linkBoundaryGuard"), + appendTransaction(transactions, _oldState, newState) { + if (!transactions.some((tr) => tr.docChanged)) return null; + const linkType = newState.schema.marks.link; + if (!linkType) return null; + let tr: ReturnType | null = null; + newState.doc.descendants((node, pos) => { + if (!node.isText || !node.text) return; + const linkMark = node.marks.find((m) => m.type === linkType); + if (!linkMark?.attrs.href) return; + const href: string = linkMark.attrs.href; + const text = node.text; + if (text === href) return; + const hrefIndex = text.indexOf(href); + if (hrefIndex < 0) return; + if (hrefIndex > 0) { + if (!tr) tr = newState.tr; + tr.removeMark(pos, pos + hrefIndex, linkType); + } + }); + return tr; + }, + }), ]; }, }).configure({