From c197d89ddc7b925c0b00f7e62db0c4ca82d720c8 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Wed, 18 Dec 2024 03:51:03 -0800 Subject: [PATCH] Desktop: Fixes #11382: Rich Text Editor: Fix resized images in lists break sub-list items (#11532) --- .../image_preserve_size_in_lists.html | 33 +++++++++++++++++ .../image_preserve_size_in_lists.md | 11 ++++++ packages/turndown/src/commonmark-rules.js | 35 ++++++++++++++++++- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.html create mode 100644 packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.md diff --git a/packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.html b/packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.html new file mode 100644 index 000000000..2050b05ba --- /dev/null +++ b/packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.html @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.md b/packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.md new file mode 100644 index 000000000..6ca842d2f --- /dev/null +++ b/packages/app-cli/tests/html_to_md/image_preserve_size_in_lists.md @@ -0,0 +1,11 @@ +- + - HTML images just before a sublist should have a closing <img> tag added. + - Otherwise, when rendered to Markdown again, the subsequent list items will be rendered as HTML. +- + **A closing tag is also necessary if the image is followed by content on a new line.** +- ![](:/0123456789abcdef0123456789abcdef) + - Similar logic isn't necessary for images converted to Markdown. +- +- It also isn't necessary if the next item is at the same level as the image... +- ...or if the image has text just before it. + - See [this issue](https://github.com/laurent22/joplin/issues/11382) for details. \ No newline at end of file diff --git a/packages/turndown/src/commonmark-rules.js b/packages/turndown/src/commonmark-rules.js index b2a3249d2..7b94a38ad 100644 --- a/packages/turndown/src/commonmark-rules.js +++ b/packages/turndown/src/commonmark-rules.js @@ -561,7 +561,40 @@ function imageMarkdownFromNode(node, options = null) { }, options); if (options.preserveImageTagsWithSize && (node.getAttribute('width') || node.getAttribute('height'))) { - return node.outerHTML; + let html = node.outerHTML; + + // To prevent markup immediately after the image from being interpreted as HTML, a closing tag + // is sometimes necessary. + const needsClosingTag = () => { + const parent = node.parentElement; + if (!parent || parent.nodeName !== 'LI') return false; + const hasClosingTag = html.match(/<\/[a-z]+\/>$/ig); + if (hasClosingTag) { + return false; + } + + const allChildren = [...parent.childNodes]; + const nonEmptyChildren = allChildren.filter(item => { + // Even if surrounded by #text nodes that only contain whitespace, Markdown after + // an can still be incorrectly interpreted as HTML. Only non-empty #texts seem + // to prevent this. + return item.nodeName !== '#text' || item.textContent.trim() !== ''; + }); + + const imageIndex = nonEmptyChildren.indexOf(node); + const hasNextSibling = imageIndex + 1 < nonEmptyChildren.length; + const nextSiblingName = hasNextSibling ? ( + nonEmptyChildren[imageIndex + 1].nodeName + ) : null; + + const nextSiblingIsNewLine = nextSiblingName === 'UL' || nextSiblingName === 'OL' || nextSiblingName === 'BR'; + return imageIndex === 0 && nextSiblingIsNewLine; + }; + + if (needsClosingTag()) { + html = html.replace(/[/]?>$/, `>`); + } + return html; } return imageMarkdownFromAttributes({