diff --git a/package-lock.json b/package-lock.json index 45f1f53721..8f5599767c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@sveltejs/svelte-virtual-list": "^3.0.1", "@tiptap/core": "^3.0.7", "@tiptap/extension-bubble-menu": "^2.26.1", + "@tiptap/extension-code": "^3.0.7", "@tiptap/extension-code-block-lowlight": "^3.0.7", "@tiptap/extension-drag-handle": "^3.4.5", "@tiptap/extension-file-handler": "^3.0.7", @@ -3369,9 +3370,9 @@ } }, "node_modules/@tiptap/extension-collaboration": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-3.20.0.tgz", - "integrity": "sha512-JItmI4U0i4kqorO114u24hM9k945IdaQ6Uc2DEtPBFFuS8cepJf2zw+ulAT1kAx6ZRiNvNpT9M7w+J0mWRn+Sg==", + "version": "3.20.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-3.20.1.tgz", + "integrity": "sha512-JnwLvyzrutBffHp6YPnf0XyTnhAgqZ9D8JSUKFp0edvai+dxsb+UMlawesBrgAuoQXw3B8YZUo2VFDVdKas1xQ==", "license": "MIT", "peer": true, "funding": { @@ -3379,8 +3380,8 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.20.0", - "@tiptap/pm": "^3.20.0", + "@tiptap/core": "^3.20.1", + "@tiptap/pm": "^3.20.1", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } @@ -3628,9 +3629,9 @@ } }, "node_modules/@tiptap/extension-node-range": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-node-range/-/extension-node-range-3.20.0.tgz", - "integrity": "sha512-XeKKTV88VuJ4Mh0Rxvc/PPzG76cb44sE+rB4u0J/ms63R/WFTm6yJQlCgUVGnGeHleSlrWuZY8gGSuoljmQzqg==", + "version": "3.20.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-node-range/-/extension-node-range-3.20.1.tgz", + "integrity": "sha512-+W/mQJxlkXMcwldWUqwdoR0eniJ1S9cVJoAy2Lkis0NhILZDWVNGKl9J4WFoCOXn8Myr17IllIxRYvAXJJ4FHQ==", "license": "MIT", "peer": true, "funding": { @@ -3638,8 +3639,8 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.20.0", - "@tiptap/pm": "^3.20.0" + "@tiptap/core": "^3.20.1", + "@tiptap/pm": "^3.20.1" } }, "node_modules/@tiptap/extension-ordered-list": { @@ -3709,9 +3710,9 @@ } }, "node_modules/@tiptap/extension-text-style": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-3.20.0.tgz", - "integrity": "sha512-zyWW1a6W+kaXAn3wv2svJ1XuVMapujftvH7Xn2Q3QmKKiDkO+NiFkrGe8BhMopu8Im51nO3NylIgVA0X1mS1rQ==", + "version": "3.20.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-3.20.1.tgz", + "integrity": "sha512-3LQU92zX6tzl47EBskkAKeJXd6EWwYmBDE7jbd7InJqnt9NMAcj4DtXtXpI+e6Un5+8yzNjVA+fI5+5cFS3dSg==", "license": "MIT", "peer": true, "funding": { @@ -3719,7 +3720,7 @@ "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.20.0" + "@tiptap/core": "^3.20.1" } }, "node_modules/@tiptap/extension-typography": { diff --git a/package.json b/package.json index 74a2e04992..0e30237aee 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@sveltejs/svelte-virtual-list": "^3.0.1", "@tiptap/core": "^3.0.7", "@tiptap/extension-bubble-menu": "^2.26.1", + "@tiptap/extension-code": "^3.0.7", "@tiptap/extension-code-block-lowlight": "^3.0.7", "@tiptap/extension-drag-handle": "^3.4.5", "@tiptap/extension-file-handler": "^3.0.7", @@ -157,4 +158,4 @@ "node": ">=18.13.0 <=22.x.x", "npm": ">=6.0.0" } -} +} \ No newline at end of file diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte index aea8ca7191..07bc4680ea 100644 --- a/src/lib/components/common/RichTextInput.svelte +++ b/src/lib/components/common/RichTextInput.svelte @@ -114,7 +114,7 @@ import { Fragment, DOMParser } from 'prosemirror-model'; import { EditorState, Plugin, PluginKey, TextSelection, Selection } from 'prosemirror-state'; import { Decoration, DecorationSet } from 'prosemirror-view'; - import { Editor, Extension, mergeAttributes } from '@tiptap/core'; + import { Editor, Extension, markInputRule, mergeAttributes } from '@tiptap/core'; import { AIAutocompletion } from './RichTextInput/AutoCompletion.js'; @@ -135,8 +135,26 @@ import FileHandler from '@tiptap/extension-file-handler'; import Typography from '@tiptap/extension-typography'; import Highlight from '@tiptap/extension-highlight'; + import Code from '@tiptap/extension-code'; import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'; + // WORKAROUND: TipTap's default Code mark input rule regex captures the + // character before the opening backtick, causing it to be deleted. + // This uses a lookbehind assertion instead so the preceding character is + // matched for position but not captured/deleted. + // Upstream fix: https://github.com/ueberdosis/tiptap/pull/7124 + const backtickInputRegex = /(?<=\s|^)`([^`]+)`(?!`)$/; + const FixedCode = Code.extend({ + addInputRules() { + return [ + markInputRule({ + find: backtickInputRegex, + type: this.type + }) + ]; + } + }); + import Mention from '@tiptap/extension-mention'; import FormattingButtons from './RichTextInput/FormattingButtons.svelte'; @@ -692,12 +710,14 @@ extensions: [ StarterKit.configure({ link: link, + code: false, // Disabled in favor of FixedCode (see workaround above) // When rich text is off, disable Strike from StarterKit so we can // re-add it below without its Mod-Shift-s shortcut (which conflicts // with the Toggle Sidebar shortcut). When rich text is on, the user // can undo strikethrough via the toolbar, so the shortcut is fine. ...(richText ? {} : { strike: false }) }), + FixedCode, ...(dragHandle ? [ListItemDragHandle] : []), Placeholder.configure({ placeholder: () => _placeholder, showOnlyWhenEditable: false }), SelectionDecoration,