From 8f2d8dd47a45a4b3972ac8badc09cc280f84b838 Mon Sep 17 00:00:00 2001 From: adamelmore <2363879+adamdottv@users.noreply.github.com> Date: Mon, 23 Feb 2026 09:54:26 -0600 Subject: [PATCH] fix(app): duplicate markdown --- packages/ui/src/components/markdown.tsx | 156 +++++++++++------------- 1 file changed, 69 insertions(+), 87 deletions(-) diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index a5f440f81..bb41c74ef 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -103,6 +103,70 @@ function setCopyState(button: HTMLButtonElement, labels: CopyLabels, copied: boo button.setAttribute("data-tooltip", labels.copy) } +function ensureCodeWrapper(block: HTMLPreElement, labels: CopyLabels) { + const parent = block.parentElement + if (!parent) return + const wrapped = parent.getAttribute("data-component") === "markdown-code" + if (!wrapped) { + const wrapper = document.createElement("div") + wrapper.setAttribute("data-component", "markdown-code") + parent.replaceChild(wrapper, block) + wrapper.appendChild(block) + wrapper.appendChild(createCopyButton(labels)) + return + } + + const buttons = Array.from(parent.querySelectorAll('[data-slot="markdown-copy-button"]')).filter( + (el): el is HTMLButtonElement => el instanceof HTMLButtonElement, + ) + + if (buttons.length === 0) { + parent.appendChild(createCopyButton(labels)) + return + } + + for (const button of buttons.slice(1)) { + button.remove() + } +} + +function markCodeLinks(root: HTMLDivElement) { + const codeNodes = Array.from(root.querySelectorAll(":not(pre) > code")) + for (const code of codeNodes) { + const href = codeUrl(code.textContent ?? "") + const parentLink = + code.parentElement instanceof HTMLAnchorElement && code.parentElement.classList.contains("external-link") + ? code.parentElement + : null + + if (!href) { + if (parentLink) parentLink.replaceWith(code) + continue + } + + if (parentLink) { + parentLink.href = href + continue + } + + const link = document.createElement("a") + link.href = href + link.className = "external-link" + link.target = "_blank" + link.rel = "noopener noreferrer" + code.parentNode?.replaceChild(link, code) + link.appendChild(code) + } +} + +function decorate(root: HTMLDivElement, labels: CopyLabels) { + const blocks = Array.from(root.querySelectorAll("pre")) + for (const block of blocks) { + ensureCodeWrapper(block, labels) + } + markCodeLinks(root) +} + function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { const timeouts = new Map>() @@ -111,62 +175,6 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { setCopyState(button, labels, copied) } - const ensureWrapper = (block: HTMLPreElement) => { - const parent = block.parentElement - if (!parent) return - const wrapped = parent.getAttribute("data-component") === "markdown-code" - if (wrapped) { - const buttons = Array.from(parent.querySelectorAll('[data-slot="markdown-copy-button"]')).filter( - (el): el is HTMLButtonElement => el instanceof HTMLButtonElement, - ) - - if (buttons.length === 0) { - parent.appendChild(createCopyButton(labels)) - return - } - - for (const button of buttons.slice(1)) { - button.remove() - } - - return - } - const wrapper = document.createElement("div") - wrapper.setAttribute("data-component", "markdown-code") - parent.replaceChild(wrapper, block) - wrapper.appendChild(block) - wrapper.appendChild(createCopyButton(labels)) - } - - const markCodeLinks = () => { - const codeNodes = Array.from(root.querySelectorAll(":not(pre) > code")) - for (const code of codeNodes) { - const href = codeUrl(code.textContent ?? "") - const parentLink = - code.parentElement instanceof HTMLAnchorElement && code.parentElement.classList.contains("external-link") - ? code.parentElement - : null - - if (!href) { - if (parentLink) parentLink.replaceWith(code) - continue - } - - if (parentLink) { - parentLink.href = href - continue - } - - const link = document.createElement("a") - link.href = href - link.className = "external-link" - link.target = "_blank" - link.rel = "noopener noreferrer" - code.parentNode?.replaceChild(link, code) - link.appendChild(code) - } - } - const handleClick = async (event: MouseEvent) => { const target = event.target if (!(target instanceof Element)) return @@ -186,11 +194,7 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { timeouts.set(button, timeout) } - const blocks = Array.from(root.querySelectorAll("pre")) - for (const block of blocks) { - ensureWrapper(block) - } - markCodeLinks() + decorate(root, labels) const buttons = Array.from(root.querySelectorAll('[data-slot="markdown-copy-button"]')) for (const button of buttons) { @@ -207,20 +211,6 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { } } -function wrapCodeBlocks(root: HTMLDivElement) { - const blocks = Array.from(root.querySelectorAll("pre")) - for (const block of blocks) { - const parent = block.parentElement - if (!parent) continue - const wrapped = parent.getAttribute("data-component") === "markdown-code" - if (wrapped) continue - const wrapper = document.createElement("div") - wrapper.setAttribute("data-component", "markdown-code") - parent.replaceChild(wrapper, block) - wrapper.appendChild(block) - } -} - function touch(key: string, value: Entry) { cache.delete(key) cache.set(key, value) @@ -284,23 +274,15 @@ export function Markdown( const temp = document.createElement("div") temp.innerHTML = content - wrapCodeBlocks(temp) + decorate(temp, { + copy: i18n.t("ui.message.copy"), + copied: i18n.t("ui.message.copied"), + }) morphdom(container, temp, { childrenOnly: true, onBeforeElUpdated: (fromEl, toEl) => { if (fromEl.isEqualNode(toEl)) return false - - const fromWrapped = fromEl.getAttribute("data-component") === "markdown-code" - const toWrapped = toEl.getAttribute("data-component") === "markdown-code" - if (fromWrapped && toWrapped) { - const fromPre = fromEl.querySelector("pre") - const toPre = toEl.querySelector("pre") - if (fromPre && toPre && !fromPre.isEqualNode(toPre)) { - morphdom(fromPre, toPre) - } - return false - } return true }, })