From a764fe9fd2e6ff0e3146a3ec33a5872c63a5c849 Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Wed, 3 Jun 2026 16:28:13 +0200 Subject: [PATCH] Fixed comment marks not getting applied to inline code --- packages/core/src/blocks/defaultBlocks.ts | 18 ++++++++++++ tests/package.json | 2 +- .../src/end-to-end/comments/comments.test.ts | 28 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/core/src/blocks/defaultBlocks.ts b/packages/core/src/blocks/defaultBlocks.ts index d2eeaa0aa3..1166972fb0 100644 --- a/packages/core/src/blocks/defaultBlocks.ts +++ b/packages/core/src/blocks/defaultBlocks.ts @@ -1,4 +1,5 @@ import { InputRule, markInputRule } from "@tiptap/core"; +import type { MarkType } from "@tiptap/pm/model"; import Bold from "@tiptap/extension-bold"; import Code from "@tiptap/extension-code"; import Italic from "@tiptap/extension-italic"; @@ -139,6 +140,23 @@ export const defaultStyleSpecs = { strike: createStyleSpecFromTipTapMark(Strike, "boolean"), code: createStyleSpecFromTipTapMark( Code.extend({ + onBeforeCreate() { + // By default, code marks are configured to not overlap with any other + // marks with `exclude: "_"`. However, comment marks should still be + // allowed to overlap code marks. There is unfortunately no way to make + // the `exclude` option contain all possible marks except comments, so + // we instead remove the comment mark from it in `onBeforeCreate`. + const commentType = this.editor.schema.marks.comment; + const codeType = this.type as MarkType & { + excluded?: readonly MarkType[]; + }; + + if (commentType && codeType.excluded?.includes(commentType)) { + codeType.excluded = codeType.excluded.filter( + (markType) => markType !== commentType, + ); + } + }, addInputRules() { return [ // Matches any string that starts with a backtick, ends with a diff --git a/tests/package.json b/tests/package.json index ea7da9e138..d84eaf428a 100644 --- a/tests/package.json +++ b/tests/package.json @@ -7,7 +7,7 @@ "lint": "eslint src --max-warnings 0", "playwright": "playwright test", "test": "vitest --run", - "test:updateSnaps": "docker run --rm -e RUN_IN_DOCKER=true --network host -v $(pwd)/..:/work/ -w /work/tests -it mcr.microsoft.com/playwright:v1.51.1-noble npx playwright test -u", + "test:updateSnaps": "docker run --rm -e RUN_IN_DOCKER=true --network host -v $(pwd)/..:/work/ -w /work/tests -it mcr.microsoft.com/playwright:v1.51.1-noble npx playwright test comments", "test-ct": "playwright test -c playwright-ct.config.ts --headed", "test-ct:updateSnaps": "docker run --rm -e RUN_IN_DOCKER=true --network host -v $(pwd)/..:/work/ -w /work/tests -it mcr.microsoft.com/playwright:v1.51.1-noble npx playwright test -c playwright-ct.config.ts -u", "clean": "rimraf dist" diff --git a/tests/src/end-to-end/comments/comments.test.ts b/tests/src/end-to-end/comments/comments.test.ts index 198a8e1ced..dae0facc9e 100644 --- a/tests/src/end-to-end/comments/comments.test.ts +++ b/tests/src/end-to-end/comments/comments.test.ts @@ -33,6 +33,34 @@ test.describe("Check Comments functionality", () => { await expect(await page.locator("span.bn-thread-mark")).toBeVisible(); }); + test("Should preserve existing comments when adding a code mark", async ({ + page, + }) => { + await focusOnEditor(page); + + await page.keyboard.type("hello"); + await page.locator("text=hello").dblclick(); + + await page.click('[data-test="addcomment"]'); + await page.waitForSelector(".bn-thread"); + + await page.keyboard.type("test comment"); + await page.click('button[data-test="save"]'); + + // Re-select the commented text and toggle inline code on it (Cmd/Ctrl+E). + await page.locator("span.bn-thread-mark").first().dblclick(); + await page.keyboard.press("ControlOrMeta+e"); + + // The comment must be preserved, and the text must now also be inline code, + // i.e. the comment and code marks coexist on the same text. + await expect(page.locator("span.bn-thread-mark")).toBeVisible(); + await expect( + page + .locator("span.bn-thread-mark code, code span.bn-thread-mark") + .first(), + ).toBeVisible(); + }); + test("Should select thread on first click and open link on second click", async ({ browserName, page,