···11+// from https://github.com/Doist/typist/blob/main/src/extensions/rich-text/rich-text-link.ts
22+import { InputRule, markInputRule, markPasteRule, PasteRule } from '@tiptap/core';
33+import { Link } from '@tiptap/extension-link';
44+55+import type { LinkOptions } from '@tiptap/extension-link';
66+77+/**
88+ * The input regex for Markdown links with title support, and multiple quotation marks (required
99+ * in case the `Typography` extension is being included).
1010+ */
1111+const inputRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)$/i;
1212+1313+/**
1414+ * The paste regex for Markdown links with title support, and multiple quotation marks (required
1515+ * in case the `Typography` extension is being included).
1616+ */
1717+const pasteRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)/gi;
1818+1919+/**
2020+ * Input rule built specifically for the `Link` extension, which ignores the auto-linked URL in
2121+ * parentheses (e.g., `(https://doist.dev)`).
2222+ *
2323+ * @see https://github.com/ueberdosis/tiptap/discussions/1865
2424+ */
2525+function linkInputRule(config: Parameters<typeof markInputRule>[0]) {
2626+ const defaultMarkInputRule = markInputRule(config);
2727+2828+ return new InputRule({
2929+ find: config.find,
3030+ handler(props) {
3131+ const { tr } = props.state;
3232+3333+ defaultMarkInputRule.handler(props);
3434+ tr.setMeta('preventAutolink', true);
3535+ }
3636+ });
3737+}
3838+3939+/**
4040+ * Paste rule built specifically for the `Link` extension, which ignores the auto-linked URL in
4141+ * parentheses (e.g., `(https://doist.dev)`). This extension was inspired from the multiple
4242+ * implementations found in a Tiptap discussion at GitHub.
4343+ *
4444+ * @see https://github.com/ueberdosis/tiptap/discussions/1865
4545+ */
4646+function linkPasteRule(config: Parameters<typeof markPasteRule>[0]) {
4747+ const defaultMarkPasteRule = markPasteRule(config);
4848+4949+ return new PasteRule({
5050+ find: config.find,
5151+ handler(props) {
5252+ const { tr } = props.state;
5353+5454+ defaultMarkPasteRule.handler(props);
5555+ tr.setMeta('preventAutolink', true);
5656+ }
5757+ });
5858+}
5959+6060+/**
6161+ * The options available to customize the `RichTextLink` extension.
6262+ */
6363+type RichTextLinkOptions = LinkOptions;
6464+6565+/**
6666+ * Custom extension that extends the built-in `Link` extension to add additional input/paste rules
6767+ * for converting the Markdown link syntax (i.e. `[Doist](https://doist.com)`) into links, and also
6868+ * adds support for the `title` attribute.
6969+ */
7070+const RichTextLink = Link.extend<RichTextLinkOptions>({
7171+ inclusive: false,
7272+ addOptions() {
7373+ return {
7474+ ...this.parent?.(),
7575+ openOnClick: 'whenNotEditable' as const
7676+ } as RichTextLinkOptions;
7777+ },
7878+ addAttributes() {
7979+ return {
8080+ ...this.parent?.(),
8181+ title: {
8282+ default: null
8383+ }
8484+ };
8585+ },
8686+ addInputRules() {
8787+ return [
8888+ linkInputRule({
8989+ find: inputRegex,
9090+ type: this.type,
9191+9292+ // We need to use `pop()` to remove the last capture groups from the match to
9393+ // satisfy Tiptap's `markPasteRule` expectation of having the content as the last
9494+ // capture group in the match (this makes the attribute order important)
9595+ getAttributes(match) {
9696+ return {
9797+ title: match.pop()?.trim(),
9898+ href: match.pop()?.trim()
9999+ };
100100+ }
101101+ })
102102+ ];
103103+ },
104104+ addPasteRules() {
105105+ return [
106106+ linkPasteRule({
107107+ find: pasteRegex,
108108+ type: this.type,
109109+110110+ // We need to use `pop()` to remove the last capture groups from the match to
111111+ // satisfy Tiptap's `markInputRule` expectation of having the content as the last
112112+ // capture group in the match (this makes the attribute order important)
113113+ getAttributes(match) {
114114+ return {
115115+ title: match.pop()?.trim(),
116116+ href: match.pop()?.trim()
117117+ };
118118+ }
119119+ })
120120+ ];
121121+ }
122122+});
123123+124124+export { RichTextLink };
125125+126126+export type { RichTextLinkOptions };