From 399fec770f4f56c1df85a6f2d4858bc3f8c11c9a Mon Sep 17 00:00:00 2001 From: Rahul A Mistry <149420892+ProdigyRahul@users.noreply.github.com> Date: Sun, 25 Jan 2026 05:59:58 +0530 Subject: fix(app): markdown rendering with morphdom for better dom functions (#10373) --- packages/ui/package.json | 1 + packages/ui/src/components/markdown.tsx | 55 +++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) (limited to 'packages/ui') 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 | 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 (
-- cgit v1.2.3