summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src
diff options
context:
space:
mode:
authoradamelmore <[email protected]>2026-02-23 09:54:26 -0600
committeradamelmore <[email protected]>2026-02-23 09:54:26 -0600
commit8f2d8dd47a45a4b3972ac8badc09cc280f84b838 (patch)
treeb8572866b45b4d953c5636088605d13bca99759d /packages/ui/src
parent3b5b21a91e2a8f084ee8ed85aca81246880a9384 (diff)
downloadopencode-8f2d8dd47a45a4b3972ac8badc09cc280f84b838.tar.gz
opencode-8f2d8dd47a45a4b3972ac8badc09cc280f84b838.zip
fix(app): duplicate markdown
Diffstat (limited to 'packages/ui/src')
-rw-r--r--packages/ui/src/components/markdown.tsx148
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
},
})