From 5de1c8e3eda9c9fd31b76ad17f1c6851fe1702fd Mon Sep 17 00:00:00 2001 From: Olivier Lambert Date: Tue, 24 Feb 2026 16:51:24 +0100 Subject: [PATCH] fix: inline code input rule deletes character before opening backtick (#1923) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream TipTap Code extension input rule regex /(^|[^`])`([^`]+)`(?!`)$/ uses a capture group (^|[^`]) that includes the character preceding the opening backtick in the full match. When markInputRule processes this, it deletes everything from the match start to the code content, which removes that preceding character along with the backtick delimiters. For example, typing foo(`bar` would result in foo`bar` (formatted) instead of the expected foo(`bar` (formatted) — the ( is lost. Fix: disable the built-in Code extension from StarterKit and register it separately with a corrected regex that uses a lookbehind assertion (?:^|(?<=[^`])) instead of a capture group. The lookbehind asserts the preceding character without including it in the match, so markInputRule only deletes the backtick delimiters. Functionally tested on Firefox and Chrome. Co-authored-by: Claude Opus 4.6 --- .../features/editor/extensions/extensions.ts | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index 687e76f9..9610e791 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -1,4 +1,6 @@ +import { markInputRule } from "@tiptap/core"; import { StarterKit } from "@tiptap/starter-kit"; +import { Code } from "@tiptap/extension-code"; import { TextAlign } from "@tiptap/extension-text-align"; import { TaskList, TaskItem } from "@tiptap/extension-list"; import { Placeholder, CharacterCount } from "@tiptap/extensions"; @@ -113,10 +115,24 @@ export const mainExtensions = [ color: "#70CFF8", }, codeBlock: false, - code: { - HTMLAttributes: { - spellcheck: false, - }, + code: false, + }), + // Override TipTap's Code extension to fix the inline code input rule. + // The upstream regex /(^|[^`])`([^`]+)`(?!`)$/ captures the character + // before the opening backtick as part of the match, causing markInputRule + // to delete it. Using a lookbehind avoids including it in the match. + Code.configure({ + HTMLAttributes: { + spellcheck: false, + }, + }).extend({ + addInputRules() { + return [ + markInputRule({ + find: /(?:^|(?<=[^`]))`([^`]+)`(?!`)$/, + type: this.type, + }), + ]; }, }), SharedStorage,