diff options
| author | Rahul A Mistry <[email protected]> | 2026-01-25 05:59:58 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-01-24 18:29:58 -0600 |
| commit | 399fec770f4f56c1df85a6f2d4858bc3f8c11c9a (patch) | |
| tree | f25fad49b5b7842abf7f7d88ab76314e0da6d6ea /packages | |
| parent | 8d1a66d04333edda5288007cb7fbdae40d204d86 (diff) | |
| download | opencode-399fec770f4f56c1df85a6f2d4858bc3f8c11c9a.tar.gz opencode-399fec770f4f56c1df85a6f2d4858bc3f8c11c9a.zip | |
fix(app): markdown rendering with morphdom for better dom functions (#10373)
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/ui/package.json | 1 | ||||
| -rw-r--r-- | packages/ui/src/components/markdown.tsx | 55 |
2 files changed, 50 insertions, 6 deletions
diff --git a/packages/ui/package.json b/packages/ui/package.json index 20709f160..f384797f4 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -56,6 +56,7 @@ "marked": "catalog:", "marked-katex-extension": "5.1.6", "marked-shiki": "catalog:", + "morphdom": "2.7.8", "remeda": "catalog:", "shiki": "catalog:", "solid-js": "catalog:", diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index f7a1ec16f..e3102214b 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -1,6 +1,7 @@ import { useMarked } from "../context/marked" import { useI18n } from "../context/i18n" import DOMPurify from "dompurify" +import morphdom from "morphdom" import { checksum } from "@opencode-ai/util/encode" import { ComponentProps, createEffect, createResource, createSignal, onCleanup, splitProps } from "solid-js" import { isServer } from "solid-js/web" @@ -194,18 +195,61 @@ export function Markdown( { initialValue: "" }, ) + let copySetupTimer: ReturnType<typeof setTimeout> | undefined + let copyCleanup: (() => void) | undefined + createEffect(() => { const container = root() const content = html() if (!container) return - if (!content) return if (isServer) return - const cleanup = setupCodeCopy(container, { - copy: i18n.t("ui.message.copy"), - copied: i18n.t("ui.message.copied"), + + if (!content) { + container.innerHTML = "" + return + } + + const temp = document.createElement("div") + temp.innerHTML = content + + morphdom(container, temp, { + childrenOnly: true, + onBeforeElUpdated: (fromEl, toEl) => { + if (fromEl.isEqualNode(toEl)) return false + if (fromEl.getAttribute("data-component") === "markdown-code") { + const fromPre = fromEl.querySelector("pre") + const toPre = toEl.querySelector("pre") + if (fromPre && toPre && !fromPre.isEqualNode(toPre)) { + morphdom(fromPre, toPre) + } + return false + } + return true + }, + onBeforeNodeDiscarded: (node) => { + if (node instanceof Element) { + if (node.getAttribute("data-slot") === "markdown-copy-button") return false + if (node.getAttribute("data-component") === "markdown-code") return false + } + return true + }, }) - onCleanup(cleanup) + + if (copySetupTimer) clearTimeout(copySetupTimer) + copySetupTimer = setTimeout(() => { + if (copyCleanup) copyCleanup() + copyCleanup = setupCodeCopy(container, { + copy: i18n.t("ui.message.copy"), + copied: i18n.t("ui.message.copied"), + }) + }, 150) }) + + onCleanup(() => { + if (copySetupTimer) clearTimeout(copySetupTimer) + if (copyCleanup) copyCleanup() + }) + return ( <div data-component="markdown" @@ -213,7 +257,6 @@ export function Markdown( ...(local.classList ?? {}), [local.class ?? ""]: !!local.class, }} - innerHTML={html.latest} ref={setRoot} {...others} /> |
