mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
fix(app): keep list numbers when a copy crosses a code block nested in an item (#6158)
A selection starting after an item's marker demotes that item to a paragraph.
When the item holds a code block, Turndown's HTML re-parse splits the paragraph
into several siblings, and counting siblings numbered the next item too high
("4. Done." instead of "2. Done."). Each item now carries its number before
the re-parse.
Closes #6140
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -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 = [
|
||||
'<div data-paseo-markdown-tag="ol" data-paseo-markdown-list-start="1">',
|
||||
'<div data-paseo-markdown-tag="li">',
|
||||
'<div data-paseo-markdown-ignore="true" data-paseo-markdown-list-marker="true">1.</div>',
|
||||
"<div><div><span>Install it:</span></div>",
|
||||
'<div data-paseo-markdown-tag="pre" data-paseo-markdown-language="sh">',
|
||||
'<div data-paseo-markdown-tag="code"><span>brew install foo</span></div>',
|
||||
'<button data-paseo-markdown-ignore="true">Copy</button>',
|
||||
"</div></div>",
|
||||
"</div>",
|
||||
'<div data-paseo-markdown-tag="li">',
|
||||
'<div data-paseo-markdown-ignore="true" data-paseo-markdown-list-marker="true">2.</div>',
|
||||
"<div><div><span>Done.</span></div></div>",
|
||||
"</div>",
|
||||
"</div>",
|
||||
].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("<div>2. Done.</div>");
|
||||
});
|
||||
|
||||
it("retains nested markers when a drag includes the complete outer item", () => {
|
||||
const message = mountFixture();
|
||||
const firstItem = fixtureElement(message, '[data-paseo-markdown-tag="li"]');
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user