From ed4aa149df4270aea908ed598be13099a694609d Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Thu, 30 Apr 2026 17:20:08 +0200 Subject: [PATCH 1/5] Made first click of overlapping comment/link only show comment and added test --- packages/core/src/comments/extension.ts | 190 ++++++++++-------- .../src/end-to-end/comments/comments.test.ts | 47 ++++- 2 files changed, 151 insertions(+), 86 deletions(-) diff --git a/packages/core/src/comments/extension.ts b/packages/core/src/comments/extension.ts index 515a740c34..1ed258b5b6 100644 --- a/packages/core/src/comments/extension.ts +++ b/packages/core/src/comments/extension.ts @@ -1,3 +1,4 @@ +import { Extension } from "@tiptap/core"; import { Node } from "prosemirror-model"; import { Plugin, PluginKey } from "prosemirror-state"; import { Decoration, DecorationSet } from "prosemirror-view"; @@ -158,101 +159,121 @@ export const CommentsExtension = createExtension( return { key: "comments", store, - prosemirrorPlugins: [ - new Plugin({ - key: PLUGIN_KEY, - state: { - init() { - return { - decorations: DecorationSet.empty, - }; - }, - apply(tr, state) { - const action = tr.getMeta(PLUGIN_KEY); + tiptapExtensions: [ + CommentMark, + Extension.create({ + name: "comments", - if (!tr.docChanged && !action) { - return state; - } + priority: 1500, - // only update threadPositions if the doc changed - const newThreadPositions = tr.docChanged - ? getUpdatedThreadPositions(tr.doc, markType) - : store.state.threadPositions; + addProseMirrorPlugins() { + return [ + new Plugin({ + key: PLUGIN_KEY, + state: { + init() { + return { + decorations: DecorationSet.empty, + }; + }, + apply(tr, state) { + const action = tr.getMeta(PLUGIN_KEY); - if ( - newThreadPositions.size > 0 || - store.state.threadPositions.size > 0 - ) { - // small optimization; don't emit event if threadPositions before / after were both empty - store.setState((prev) => ({ - ...prev, - threadPositions: newThreadPositions, - })); - } + if (!tr.docChanged && !action) { + return state; + } - // update decorations if doc or selected thread changed - const decorations = [] as any[]; + // only update threadPositions if the doc changed + const newThreadPositions = tr.docChanged + ? getUpdatedThreadPositions(tr.doc, markType) + : store.state.threadPositions; - if (store.state.selectedThreadId) { - const selectedThreadPosition = newThreadPositions.get( - store.state.selectedThreadId, - ); + if ( + newThreadPositions.size > 0 || + store.state.threadPositions.size > 0 + ) { + // small optimization; don't emit event if threadPositions before / after were both empty + store.setState((prev) => ({ + ...prev, + threadPositions: newThreadPositions, + })); + } - if (selectedThreadPosition) { - decorations.push( - Decoration.inline( - selectedThreadPosition.from, - selectedThreadPosition.to, - { - class: "bn-thread-mark-selected", - }, - ), - ); - } - } + // update decorations if doc or selected thread changed + const decorations = [] as any[]; - return { - decorations: DecorationSet.create(tr.doc, decorations), - }; - }, - }, - props: { - decorations(state) { - return ( - PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty - ); - }, - handleClick: (view, pos, event) => { - if (event.button !== 0) { - return; - } + if (store.state.selectedThreadId) { + const selectedThreadPosition = newThreadPositions.get( + store.state.selectedThreadId, + ); - const node = view.state.doc.nodeAt(pos); + if (selectedThreadPosition) { + decorations.push( + Decoration.inline( + selectedThreadPosition.from, + selectedThreadPosition.to, + { + class: "bn-thread-mark-selected", + }, + ), + ); + } + } - if (!node) { - // unselect - store.setState((prev) => ({ - ...prev, - selectedThreadId: undefined, - })); - return; - } + return { + decorations: DecorationSet.create(tr.doc, decorations), + }; + }, + }, + props: { + decorations(state) { + return ( + PLUGIN_KEY.getState(state)?.decorations ?? + DecorationSet.empty + ); + }, + handleClick: (view, pos, event) => { + if (event.button !== 0) { + return false; + } - const commentMark = node.marks.find( - (mark) => - mark.type.name === markType && mark.attrs.orphan !== true, - ); + const node = view.state.doc.nodeAt(pos); - const threadId = commentMark?.attrs.threadId as - | string - | undefined; - if (threadId !== store.state.selectedThreadId) { - store.setState((prev) => ({ - ...prev, - selectedThreadId: threadId, - })); - } - }, + if (!node) { + // unselect + store.setState((prev) => ({ + ...prev, + selectedThreadId: undefined, + })); + return false; + } + + const commentMark = node.marks.find( + (mark) => + mark.type.name === markType && + mark.attrs.orphan !== true, + ); + + const threadId = commentMark?.attrs.threadId as + | string + | undefined; + + // If the clicked thread is already selected, do nothing and let + // other handlers process the event (e.g. navigating a link). + if (threadId === store.state.selectedThreadId) { + return false; + } + + store.setState((prev) => ({ + ...prev, + selectedThreadId: threadId, + })); + + return true; + }, + }, + }), + ]; }, }), ], @@ -356,7 +377,6 @@ export const CommentsExtension = createExtension( }, userStore, commentEditorSchema, - tiptapExtensions: [CommentMark], } as const; }, ); diff --git a/tests/src/end-to-end/comments/comments.test.ts b/tests/src/end-to-end/comments/comments.test.ts index db29498359..cd0360e1d8 100644 --- a/tests/src/end-to-end/comments/comments.test.ts +++ b/tests/src/end-to-end/comments/comments.test.ts @@ -8,7 +8,7 @@ test.beforeEach(async ({ page }) => { }); test.describe("Check Comments functionality", () => { - test("Should preserve existing comments when adding a link", async ({ + test.skip("Should preserve existing comments when adding a link", async ({ page, }) => { await focusOnEditor(page); @@ -32,4 +32,49 @@ test.describe("Check Comments functionality", () => { await expect(await page.locator("span.bn-thread-mark")).toBeVisible(); }); + + test("Should select thread on first click and open link on second click", async ({ + browserName, + page, + }) => { + test.skip( + browserName === "webkit", + "WebKit causes absurd failures running in Docker which aren't reproducible anywhere else.", + ); + + 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"]'); + + await page.locator("span.bn-thread-mark").first().dblclick(); + + await expect(page.locator(LINK_BUTTON_SELECTOR)).toBeVisible(); + await page.click(LINK_BUTTON_SELECTOR); + + await page.keyboard.type("https://example.com"); + await page.keyboard.press("Enter"); + + await page.keyboard.press("ArrowDown"); + await page.waitForTimeout(500); + await expect(page.locator(".bn-thread-mark-selected")).toHaveCount(0); + + const link = page.locator('a[data-inline-content-type="link"]').first(); + + // First click selects the thread without navigating. + await link.click(); + await page.waitForTimeout(500); + await expect(page.locator(".bn-thread-mark-selected")).toBeVisible(); + + // Second click on the now-selected thread navigates to the link target. + const popupPromise = page.waitForEvent("popup"); + await link.click(); + await popupPromise; + }); }); From 4928547329b49e3b7c3186dddbe7f33b8c606e7b Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Fri, 1 May 2026 11:36:12 +0200 Subject: [PATCH 2/5] Refactored extensions to both be BlockNote extensions, use `runsBefore` instead of `priority` --- packages/core/src/comments/extension.ts | 193 +++++++++--------- .../managers/ExtensionManager/extensions.ts | 17 +- .../tiptap-extensions/Link/index.ts | 2 +- .../extensions/tiptap-extensions/Link/link.ts | 33 ++- .../src/end-to-end/comments/comments.test.ts | 2 +- 5 files changed, 132 insertions(+), 115 deletions(-) diff --git a/packages/core/src/comments/extension.ts b/packages/core/src/comments/extension.ts index 1ed258b5b6..b22013c78c 100644 --- a/packages/core/src/comments/extension.ts +++ b/packages/core/src/comments/extension.ts @@ -1,4 +1,3 @@ -import { Extension } from "@tiptap/core"; import { Node } from "prosemirror-model"; import { Plugin, PluginKey } from "prosemirror-state"; import { Decoration, DecorationSet } from "prosemirror-view"; @@ -159,121 +158,111 @@ export const CommentsExtension = createExtension( return { key: "comments", store, - tiptapExtensions: [ - CommentMark, - Extension.create({ - name: "comments", - - priority: 1500, - - addProseMirrorPlugins() { - return [ - new Plugin({ - key: PLUGIN_KEY, - state: { - init() { - return { - decorations: DecorationSet.empty, - }; - }, - apply(tr, state) { - const action = tr.getMeta(PLUGIN_KEY); + runsBefore: ["link"], + tiptapExtensions: [CommentMark], + prosemirrorPlugins: [ + new Plugin({ + key: PLUGIN_KEY, + state: { + init() { + return { + decorations: DecorationSet.empty, + }; + }, + apply(tr, state) { + const action = tr.getMeta(PLUGIN_KEY); - if (!tr.docChanged && !action) { - return state; - } + if (!tr.docChanged && !action) { + return state; + } - // only update threadPositions if the doc changed - const newThreadPositions = tr.docChanged - ? getUpdatedThreadPositions(tr.doc, markType) - : store.state.threadPositions; + // only update threadPositions if the doc changed + const newThreadPositions = tr.docChanged + ? getUpdatedThreadPositions(tr.doc, markType) + : store.state.threadPositions; - if ( - newThreadPositions.size > 0 || - store.state.threadPositions.size > 0 - ) { - // small optimization; don't emit event if threadPositions before / after were both empty - store.setState((prev) => ({ - ...prev, - threadPositions: newThreadPositions, - })); - } + if ( + newThreadPositions.size > 0 || + store.state.threadPositions.size > 0 + ) { + // small optimization; don't emit event if threadPositions before / after were both empty + store.setState((prev) => ({ + ...prev, + threadPositions: newThreadPositions, + })); + } - // update decorations if doc or selected thread changed - const decorations = [] as any[]; + // update decorations if doc or selected thread changed + const decorations = [] as any[]; - if (store.state.selectedThreadId) { - const selectedThreadPosition = newThreadPositions.get( - store.state.selectedThreadId, - ); + if (store.state.selectedThreadId) { + const selectedThreadPosition = newThreadPositions.get( + store.state.selectedThreadId, + ); - if (selectedThreadPosition) { - decorations.push( - Decoration.inline( - selectedThreadPosition.from, - selectedThreadPosition.to, - { - class: "bn-thread-mark-selected", - }, - ), - ); - } - } + if (selectedThreadPosition) { + decorations.push( + Decoration.inline( + selectedThreadPosition.from, + selectedThreadPosition.to, + { + class: "bn-thread-mark-selected", + }, + ), + ); + } + } - return { - decorations: DecorationSet.create(tr.doc, decorations), - }; - }, - }, - props: { - decorations(state) { - return ( - PLUGIN_KEY.getState(state)?.decorations ?? - DecorationSet.empty - ); - }, - handleClick: (view, pos, event) => { - if (event.button !== 0) { - return false; - } + return { + decorations: DecorationSet.create(tr.doc, decorations), + }; + }, + }, + props: { + decorations(state) { + return ( + PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty + ); + }, + handleClick: (view, pos, event) => { + console.log("comments"); + if (event.button !== 0) { + return false; + } - const node = view.state.doc.nodeAt(pos); + const node = view.state.doc.nodeAt(pos); - if (!node) { - // unselect - store.setState((prev) => ({ - ...prev, - selectedThreadId: undefined, - })); - return false; - } + if (!node) { + // unselect + store.setState((prev) => ({ + ...prev, + selectedThreadId: undefined, + })); + return false; + } - const commentMark = node.marks.find( - (mark) => - mark.type.name === markType && - mark.attrs.orphan !== true, - ); + const commentMark = node.marks.find( + (mark) => + mark.type.name === markType && mark.attrs.orphan !== true, + ); - const threadId = commentMark?.attrs.threadId as - | string - | undefined; + const threadId = commentMark?.attrs.threadId as + | string + | undefined; - // If the clicked thread is already selected, do nothing and let - // other handlers process the event (e.g. navigating a link). - if (threadId === store.state.selectedThreadId) { - return false; - } + // If the clicked thread is already selected, do nothing and let + // other handlers process the event (e.g. navigating a link). + if (threadId === store.state.selectedThreadId) { + return false; + } - store.setState((prev) => ({ - ...prev, - selectedThreadId: threadId, - })); + store.setState((prev) => ({ + ...prev, + selectedThreadId: threadId, + })); - return true; - }, - }, - }), - ]; + return true; + }, }, }), ], diff --git a/packages/core/src/editor/managers/ExtensionManager/extensions.ts b/packages/core/src/editor/managers/ExtensionManager/extensions.ts index 21d2d86f91..7be7070865 100644 --- a/packages/core/src/editor/managers/ExtensionManager/extensions.ts +++ b/packages/core/src/editor/managers/ExtensionManager/extensions.ts @@ -5,7 +5,7 @@ import { Extension as TiptapExtension, } from "@tiptap/core"; import { Gapcursor } from "@tiptap/extensions/gap-cursor"; -import { Link } from "../../../extensions/tiptap-extensions/Link/link.js"; +import { LinkExtension } from "../../../extensions/tiptap-extensions/Link/link.js"; import { Text } from "@tiptap/extension-text"; import { createDropFileExtension } from "../../../api/clipboard/fromClipboard/fileDropExtension.js"; import { createPasteFromClipboardExtension } from "../../../api/clipboard/fromClipboard/pasteExtension.js"; @@ -73,14 +73,6 @@ export function getDefaultTiptapExtensions( SuggestionAddMark, SuggestionDeleteMark, SuggestionModificationMark, - Link.configure({ - HTMLAttributes: options.links?.HTMLAttributes ?? {}, - editor, - onClick: options.links?.onClick, - ...(options.links?.isValidLink - ? { isValidLink: options.links.isValidLink } - : {}), - }), ...(Object.values(editor.schema.styleSpecs).map((styleSpec) => { return styleSpec.implementation.mark.configure({ editor: editor, @@ -169,6 +161,13 @@ export function getDefaultExtensions( DropCursorExtension(options), FilePanelExtension(options), FormattingToolbarExtension(options), + LinkExtension({ + HTMLAttributes: options.links?.HTMLAttributes ?? {}, + onClick: options.links?.onClick, + ...(options.links?.isValidLink + ? { isValidLink: options.links.isValidLink } + : {}), + }), LinkToolbarExtension(options), NodeSelectionKeyboardExtension(), PlaceholderExtension(options), diff --git a/packages/core/src/extensions/tiptap-extensions/Link/index.ts b/packages/core/src/extensions/tiptap-extensions/Link/index.ts index 5324cbbe38..bc54c007eb 100644 --- a/packages/core/src/extensions/tiptap-extensions/Link/index.ts +++ b/packages/core/src/extensions/tiptap-extensions/Link/index.ts @@ -1,2 +1,2 @@ -export { Link } from "./link.js"; +export { Link, LinkExtension } from "./link.js"; export { isAllowedUri } from "./link.js"; diff --git a/packages/core/src/extensions/tiptap-extensions/Link/link.ts b/packages/core/src/extensions/tiptap-extensions/Link/link.ts index 9b3405d536..cf5edade00 100644 --- a/packages/core/src/extensions/tiptap-extensions/Link/link.ts +++ b/packages/core/src/extensions/tiptap-extensions/Link/link.ts @@ -2,6 +2,7 @@ import type { PasteRuleMatch } from "@tiptap/core"; import { Mark, markPasteRule, mergeAttributes } from "@tiptap/core"; import type { Plugin } from "@tiptap/pm/state"; import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js"; +import { createExtension } from "../../../editor/BlockNoteExtension.js"; import { autolink } from "./helpers/autolink.js"; import { findLinks } from "./helpers/linkDetector.js"; import { clickHandler } from "./helpers/clickHandler.js"; @@ -74,8 +75,6 @@ export type LinkOptions = { export const Link = Mark.create({ name: "link", - priority: 1000, - keepOnSplit: false, exitable: true, @@ -205,3 +204,33 @@ export const Link = Mark.create({ return plugins; }, }); + +type LinkExtensionOptions = { + HTMLAttributes?: Record; + onClick?: ( + event: MouseEvent, + editor: BlockNoteEditor, + ) => boolean | void; + isValidLink?: (href: string) => boolean; +}; + +/** + * BlockNote extension wrapping the {@link Link} TipTap mark. Wrapping the mark + * lets other extensions order their click handlers relative to the link click + * handler via `runsBefore: ["link"]`. + */ +export const LinkExtension = createExtension( + ({ editor, options }) => { + return { + key: "link", + tiptapExtensions: [ + Link.configure({ + HTMLAttributes: options.HTMLAttributes ?? {}, + editor, + onClick: options.onClick, + ...(options.isValidLink ? { isValidLink: options.isValidLink } : {}), + }), + ], + } as const; + }, +); diff --git a/tests/src/end-to-end/comments/comments.test.ts b/tests/src/end-to-end/comments/comments.test.ts index cd0360e1d8..198a8e1ced 100644 --- a/tests/src/end-to-end/comments/comments.test.ts +++ b/tests/src/end-to-end/comments/comments.test.ts @@ -8,7 +8,7 @@ test.beforeEach(async ({ page }) => { }); test.describe("Check Comments functionality", () => { - test.skip("Should preserve existing comments when adding a link", async ({ + test("Should preserve existing comments when adding a link", async ({ page, }) => { await focusOnEditor(page); From f6b467fd1f832f106b2f86f744776728edd25e29 Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Fri, 1 May 2026 11:50:22 +0200 Subject: [PATCH 3/5] Removed log --- packages/core/src/comments/extension.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/comments/extension.ts b/packages/core/src/comments/extension.ts index b22013c78c..0a34671098 100644 --- a/packages/core/src/comments/extension.ts +++ b/packages/core/src/comments/extension.ts @@ -225,7 +225,6 @@ export const CommentsExtension = createExtension( ); }, handleClick: (view, pos, event) => { - console.log("comments"); if (event.button !== 0) { return false; } From 23b7edbe34333f13d00019dd78422460db8b729f Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Fri, 1 May 2026 11:55:16 +0200 Subject: [PATCH 4/5] Implemented PR feedback --- packages/core/src/comments/extension.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/core/src/comments/extension.ts b/packages/core/src/comments/extension.ts index 0a34671098..8d23c3e967 100644 --- a/packages/core/src/comments/extension.ts +++ b/packages/core/src/comments/extension.ts @@ -245,9 +245,20 @@ export const CommentsExtension = createExtension( mark.type.name === markType && mark.attrs.orphan !== true, ); - const threadId = commentMark?.attrs.threadId as - | string - | undefined; + if (!commentMark) { + // Clicked outside any comment thread. Deselect if needed but + // don't consume the event so other handlers (e.g. link + // navigation) can process it. + if (store.state.selectedThreadId !== undefined) { + store.setState((prev) => ({ + ...prev, + selectedThreadId: undefined, + })); + } + return false; + } + + const threadId = commentMark.attrs.threadId as string; // If the clicked thread is already selected, do nothing and let // other handlers process the event (e.g. navigating a link). From 98c1cb3f99bc2286252387e5bfdab2020e84dc08 Mon Sep 17 00:00:00 2001 From: Matthew Lipski Date: Fri, 1 May 2026 12:02:28 +0200 Subject: [PATCH 5/5] Updated AI test snapshot --- .../xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap b/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap index 426acde29f..d863521526 100644 --- a/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap +++ b/packages/xl-ai/src/prosemirror/__snapshots__/agent.test.ts.snap @@ -12,7 +12,7 @@ exports[`agentStepToTr > Update > drop mark and link 1`] = ` "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."}]}]}]}]}", "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."}]}]}]}]}", "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."}]}]}]}]}", - "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}},{"type":"deletion","attrs":{"id":null}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", + "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold text. "},{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}},{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", ] `; @@ -31,7 +31,7 @@ exports[`agentStepToTr > Update > drop mark and link and change text within mark "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."}]}]}]}]}", "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."}]}]}]}]}", "S {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."}]}]}]}]}", - "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}},{"type":"deletion","attrs":{"id":null}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", + "R {"type":"doc","content":[{"type":"blockGroup","content":[{"type":"blockContainer","attrs":{"id":"ref1"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, world!"}]}]},{"type":"blockContainer","attrs":{"id":"ref2"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","text":"Hello, "},{"type":"mention","attrs":{"user":"John Doe"}},{"type":"text","text":"! "},{"type":"text","marks":[{"type":"bold"}],"text":"How are you doing?"},{"type":"text","text":" "},{"type":"text","marks":[{"type":"textColor","attrs":{"stringValue":"blue"}}],"text":"This text is blue!"}]}]},{"type":"blockContainer","attrs":{"id":"ref3"},"content":[{"type":"paragraph","attrs":{"backgroundColor":"default","textColor":"default","textAlignment":"left"},"content":[{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}}],"text":"Hello"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Hi"},{"type":"text","text":", world! "},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":"Bold"},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Bold the"},{"type":"text","marks":[{"type":"bold"},{"type":"deletion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":" text. "},{"type":"text","marks":[{"type":"deletion","attrs":{"id":null}},{"type":"link","attrs":{"href":"https://www.google.com","target":"_blank","rel":"noopener noreferrer nofollow"}}],"text":"Link."},{"type":"text","marks":[{"type":"insertion","attrs":{"id":null}}],"text":"Link."}]}]}]}]}", ] `;