From cdba17e9fc64b17425edf576fe7ac36f8dc1cf6a Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Thu, 30 Apr 2026 17:59:54 +0200 Subject: [PATCH 1/2] Added edge case handling for code mark input rule --- packages/core/src/blocks/defaultBlocks.ts | 28 ++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/core/src/blocks/defaultBlocks.ts b/packages/core/src/blocks/defaultBlocks.ts index 459b987f00..52595e8d3c 100644 --- a/packages/core/src/blocks/defaultBlocks.ts +++ b/packages/core/src/blocks/defaultBlocks.ts @@ -1,3 +1,4 @@ +import { InputRule } from "@tiptap/core"; import Bold from "@tiptap/extension-bold"; import Code from "@tiptap/extension-code"; import Italic from "@tiptap/extension-italic"; @@ -136,7 +137,32 @@ export const defaultStyleSpecs = { italic: createStyleSpecFromTipTapMark(Italic, "boolean"), underline: createStyleSpecFromTipTapMark(Underline, "boolean"), strike: createStyleSpecFromTipTapMark(Strike, "boolean"), - code: createStyleSpecFromTipTapMark(Code, "boolean"), + code: createStyleSpecFromTipTapMark( + Code.extend({ + // Extends the Code mark with an extra input rule that fires when a space is + // typed after the closing backtick. The default rule only fires when typing + // the closing backtick itself, so it misses the case where the user opens + // both backticks first, then writes content between them. + addInputRules() { + return [ + ...(this.parent?.() ?? []), + new InputRule({ + find: /(^|[^`])`([^`]+)`(?!`) $/, + handler: ({ state, range, match }) => { + const { tr, schema } = state; + const leadingChar = match[1]; + const content = match[2]; + tr.replaceWith(range.from + leadingChar.length, range.to, [ + schema.text(content, [this.type.create()]), + schema.text(" "), + ]); + }, + }), + ]; + }, + }), + "boolean", + ), textColor: TextColor, backgroundColor: BackgroundColor, } satisfies StyleSpecs; From 112985ef4240cd96e0c497eb0b88e79fc0445aac Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Fri, 1 May 2026 11:48:16 +0200 Subject: [PATCH 2/2] Explicitly added original input rule instead of inheriting --- packages/core/src/blocks/defaultBlocks.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/core/src/blocks/defaultBlocks.ts b/packages/core/src/blocks/defaultBlocks.ts index 52595e8d3c..d2eeaa0aa3 100644 --- a/packages/core/src/blocks/defaultBlocks.ts +++ b/packages/core/src/blocks/defaultBlocks.ts @@ -1,4 +1,4 @@ -import { InputRule } from "@tiptap/core"; +import { InputRule, markInputRule } from "@tiptap/core"; import Bold from "@tiptap/extension-bold"; import Code from "@tiptap/extension-code"; import Italic from "@tiptap/extension-italic"; @@ -139,13 +139,20 @@ export const defaultStyleSpecs = { strike: createStyleSpecFromTipTapMark(Strike, "boolean"), code: createStyleSpecFromTipTapMark( Code.extend({ - // Extends the Code mark with an extra input rule that fires when a space is - // typed after the closing backtick. The default rule only fires when typing - // the closing backtick itself, so it misses the case where the user opens - // both backticks first, then writes content between them. addInputRules() { return [ - ...(this.parent?.() ?? []), + // Matches any string that starts with a backtick, ends with a + // backtick, and has any non-backtick characters in between. Copied + // from original input rule: + // https://github.com/ueberdosis/tiptap/blob/c27661c148cdbea9e1c80107e10d0a9d1775c4ec/packages/extension-code/src/code.ts#L116 + markInputRule({ + find: /(^|[^`])`([^`]+)`(?!`)$/, + type: this.type, + }), + // Extends the Code mark with an extra input rule that fires when a space is + // typed after the closing backtick. The default rule only fires when typing + // the closing backtick itself, so it misses the case where the user adds + // both backticks first, then writes content between them. new InputRule({ find: /(^|[^`])`([^`]+)`(?!`) $/, handler: ({ state, range, match }) => {