diff options
| author | adamelmore <[email protected]> | 2026-02-23 09:54:26 -0600 |
|---|---|---|
| committer | adamelmore <[email protected]> | 2026-02-23 09:54:26 -0600 |
| commit | 8f2d8dd47a45a4b3972ac8badc09cc280f84b838 (patch) | |
| tree | b8572866b45b4d953c5636088605d13bca99759d /packages/ui/src | |
| parent | 3b5b21a91e2a8f084ee8ed85aca81246880a9384 (diff) | |
| download | opencode-8f2d8dd47a45a4b3972ac8badc09cc280f84b838.tar.gz opencode-8f2d8dd47a45a4b3972ac8badc09cc280f84b838.zip | |
fix(app): duplicate markdown
Diffstat (limited to 'packages/ui/src')
| -rw-r--r-- | packages/ui/src/components/markdown.tsx | 148 |
1 files changed, 65 insertions, 83 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,68 +103,76 @@ function setCopyState(button: HTMLButtonElement, labels: CopyLabels, copied: boo button.setAttribute("data-tooltip", labels.copy) } -function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) { - const timeouts = new Map<HTMLButtonElement, ReturnType<typeof setTimeout>>() - - const updateLabel = (button: HTMLButtonElement) => { - const copied = button.getAttribute("data-copied") === "true" - 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 - } +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 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 - } + const buttons = Array.from(parent.querySelectorAll('[data-slot="markdown-copy-button"]')).filter( + (el): el is HTMLButtonElement => el instanceof HTMLButtonElement, + ) - if (parentLink) { - parentLink.href = href - continue - } + 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 + } - 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) + 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<HTMLButtonElement, ReturnType<typeof setTimeout>>() + + const updateLabel = (button: HTMLButtonElement) => { + const copied = button.getAttribute("data-copied") === "true" + setCopyState(button, labels, copied) } const handleClick = async (event: MouseEvent) => { @@ -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 }, }) |
