your personal website on atproto - mirror blento.app
at fix-cached-posts 126 lines 3.6 kB view raw
1// from https://github.com/Doist/typist/blob/main/src/extensions/rich-text/rich-text-link.ts 2import { InputRule, markInputRule, markPasteRule, PasteRule } from '@tiptap/core'; 3import { Link } from '@tiptap/extension-link'; 4 5import type { LinkOptions } from '@tiptap/extension-link'; 6 7/** 8 * The input regex for Markdown links with title support, and multiple quotation marks (required 9 * in case the `Typography` extension is being included). 10 */ 11const inputRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)$/i; 12 13/** 14 * The paste regex for Markdown links with title support, and multiple quotation marks (required 15 * in case the `Typography` extension is being included). 16 */ 17const pasteRegex = /(?:^|\s)\[([^\]]*)?\]\((\S+)(?: ["“](.+)["”])?\)/gi; 18 19/** 20 * Input rule built specifically for the `Link` extension, which ignores the auto-linked URL in 21 * parentheses (e.g., `(https://doist.dev)`). 22 * 23 * @see https://github.com/ueberdosis/tiptap/discussions/1865 24 */ 25function linkInputRule(config: Parameters<typeof markInputRule>[0]) { 26 const defaultMarkInputRule = markInputRule(config); 27 28 return new InputRule({ 29 find: config.find, 30 handler(props) { 31 const { tr } = props.state; 32 33 defaultMarkInputRule.handler(props); 34 tr.setMeta('preventAutolink', true); 35 } 36 }); 37} 38 39/** 40 * Paste rule built specifically for the `Link` extension, which ignores the auto-linked URL in 41 * parentheses (e.g., `(https://doist.dev)`). This extension was inspired from the multiple 42 * implementations found in a Tiptap discussion at GitHub. 43 * 44 * @see https://github.com/ueberdosis/tiptap/discussions/1865 45 */ 46function linkPasteRule(config: Parameters<typeof markPasteRule>[0]) { 47 const defaultMarkPasteRule = markPasteRule(config); 48 49 return new PasteRule({ 50 find: config.find, 51 handler(props) { 52 const { tr } = props.state; 53 54 defaultMarkPasteRule.handler(props); 55 tr.setMeta('preventAutolink', true); 56 } 57 }); 58} 59 60/** 61 * The options available to customize the `RichTextLink` extension. 62 */ 63type RichTextLinkOptions = LinkOptions; 64 65/** 66 * Custom extension that extends the built-in `Link` extension to add additional input/paste rules 67 * for converting the Markdown link syntax (i.e. `[Doist](https://doist.com)`) into links, and also 68 * adds support for the `title` attribute. 69 */ 70const RichTextLink = Link.extend<RichTextLinkOptions>({ 71 inclusive: false, 72 addOptions() { 73 return { 74 ...this.parent?.(), 75 openOnClick: 'whenNotEditable' as const 76 } as RichTextLinkOptions; 77 }, 78 addAttributes() { 79 return { 80 ...this.parent?.(), 81 title: { 82 default: null 83 } 84 }; 85 }, 86 addInputRules() { 87 return [ 88 linkInputRule({ 89 find: inputRegex, 90 type: this.type, 91 92 // We need to use `pop()` to remove the last capture groups from the match to 93 // satisfy Tiptap's `markPasteRule` expectation of having the content as the last 94 // capture group in the match (this makes the attribute order important) 95 getAttributes(match) { 96 return { 97 title: match.pop()?.trim(), 98 href: match.pop()?.trim() 99 }; 100 } 101 }) 102 ]; 103 }, 104 addPasteRules() { 105 return [ 106 linkPasteRule({ 107 find: pasteRegex, 108 type: this.type, 109 110 // We need to use `pop()` to remove the last capture groups from the match to 111 // satisfy Tiptap's `markInputRule` expectation of having the content as the last 112 // capture group in the match (this makes the attribute order important) 113 getAttributes(match) { 114 return { 115 title: match.pop()?.trim(), 116 href: match.pop()?.trim() 117 }; 118 } 119 }) 120 ]; 121 } 122}); 123 124export { RichTextLink }; 125 126export type { RichTextLinkOptions };