diff --git a/packages/app/src/assistant-selection-copy/content.browser.test.ts b/packages/app/src/assistant-selection-copy/content.browser.test.ts
index 00a64406ea..995927f106 100644
--- a/packages/app/src/assistant-selection-copy/content.browser.test.ts
+++ b/packages/app/src/assistant-selection-copy/content.browser.test.ts
@@ -320,6 +320,32 @@ describe("assistant selection copy ranges", () => {
).toBe("6. Second bullet text");
});
+ it("keeps the next number when a drag crosses a code block nested in an item", () => {
+ const message = mountFixture();
+ const list = fixtureElement(message, '[data-paseo-markdown-tag="ul"]');
+ list.outerHTML = [
+ '
',
+ '
',
+ '
1.
',
+ "
Install it:
",
+ '
',
+ '
brew install foo
',
+ '
',
+ "
",
+ "
",
+ '
",
+ "
",
+ ].join("");
+
+ const content = copyAcross(message, "Install it:", "Done.");
+
+ expect(content?.plainText).toBe("Install it:\n\n```sh\nbrew install foo\n```\n\n2. Done.");
+ expect(content?.html).toContain("2. Done.
");
+ });
+
it("retains nested markers when a drag includes the complete outer item", () => {
const message = mountFixture();
const firstItem = fixtureElement(message, '[data-paseo-markdown-tag="li"]');
diff --git a/packages/app/src/assistant-selection-copy/content.web.ts b/packages/app/src/assistant-selection-copy/content.web.ts
index aa6e6cd91b..ac4643b2ff 100644
--- a/packages/app/src/assistant-selection-copy/content.web.ts
+++ b/packages/app/src/assistant-selection-copy/content.web.ts
@@ -53,9 +53,7 @@ turndown.addRule("compactListItem", {
if (parent?.nodeName !== "OL") {
return `${options.bulletListMarker} ${item}\n`;
}
- const start = Number(parent.getAttribute("start") ?? 1);
- const index = Array.from(parent.children).indexOf(node);
- return `${start + index}. ${item}\n`;
+ return `${node.getAttribute("value")}. ${item}\n`;
},
});
@@ -536,6 +534,7 @@ function restoreMarkdownElements(container: HTMLElement): void {
if (start) {
semanticElement.setAttribute("start", start);
}
+ numberOrderedListItems(semanticElement, Number(start ?? 1));
}
if (tagName === "pre") {
const language = element.getAttribute(MARKDOWN_COPY_LANGUAGE_ATTRIBUTE);
@@ -568,6 +567,20 @@ function restoreMarkdownElements(container: HTMLElement): void {
}
}
+/**
+ * Each item carries its number before Turndown re-parses the HTML. Until then every
+ * child of the list is one item, including a partly selected item demoted to a `p`.
+ * The parser splits a `p` that holds a code block into several siblings, so counting
+ * siblings afterwards numbers the next item too high.
+ */
+function numberOrderedListItems(list: Element, start: number): void {
+ Array.from(list.children).forEach((child, index) => {
+ if (child.tagName === "LI") {
+ child.setAttribute("value", String(start + index));
+ }
+ });
+}
+
function unwrapIncompleteTables(container: HTMLElement): void {
for (const table of container.querySelectorAll("table")) {
if (hasUsableTableColumnContract(table)) {