import StarterKit from "@tiptap/starter-kit"; import HorizontalRule from "@tiptap/extension-horizontal-rule"; import TiptapLink from "@tiptap/extension-link"; import TiptapImage from "@tiptap/extension-image"; import Placeholder from "@tiptap/extension-placeholder"; import TiptapUnderline from "@tiptap/extension-underline"; import TextStyle from "@tiptap/extension-text-style"; import { Color } from "@tiptap/extension-color"; import SlashCommand from "./slash-command"; import { InputRule } from "@tiptap/core"; export const TiptapExtensions = [ StarterKit.configure({ bulletList: { HTMLAttributes: { class: "list-disc list-outside leading-3", }, }, orderedList: { HTMLAttributes: { class: "list-decimal list-outside leading-3", }, }, listItem: { HTMLAttributes: { class: "leading-normal", }, }, blockquote: { HTMLAttributes: { class: "border-l-4 border-stone-700", }, }, codeBlock: { HTMLAttributes: { class: "rounded-sm bg-stone-100 p-5 font-mono font-medium text-stone-800", }, }, code: { HTMLAttributes: { class: "rounded-md bg-stone-200 px-1.5 py-1 font-mono font-medium text-black", }, }, horizontalRule: false, dropcursor: { color: "#DBEAFE", width: 4, }, gapcursor: false, }), // patch to fix horizontal rule bug: https://github.com/ueberdosis/tiptap/pull/3859#issuecomment-1536799740 HorizontalRule.extend({ addInputRules() { return [ new InputRule({ find: /^(?:---|—-|___\s|\*\*\*\s)$/, handler: ({ state, range, match }) => { const attributes = {}; const { tr } = state; const start = range.from; let end = range.to; tr.insert(start - 1, this.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end)); }, }), ]; }, }).configure({ HTMLAttributes: { class: "mt-4 mb-6 border-t border-stone-300", }, }), TiptapLink.configure({ HTMLAttributes: { class: "text-stone-600 underline underline-offset-[3px] hover:text-stone-700 transition-colors cursor-pointer", }, }), TiptapImage.configure({ allowBase64: true, HTMLAttributes: { class: "rounded-lg border border-stone-200", }, }), Placeholder.configure({ placeholder: ({ node }) => { if (node.type.name === "heading") { return `Heading ${node.attrs.level}`; } return "Press '/' for commands, or '++' for AI autocomplete..."; }, includeChildren: true, }), SlashCommand, TiptapUnderline, TextStyle, Color, ];