fix(app): duplicate markdown
This commit is contained in:
@@ -103,6 +103,70 @@ function setCopyState(button: HTMLButtonElement, labels: CopyLabels, copied: boo
|
|||||||
button.setAttribute("data-tooltip", labels.copy)
|
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) {
|
function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) {
|
||||||
const timeouts = new Map<HTMLButtonElement, ReturnType<typeof setTimeout>>()
|
const timeouts = new Map<HTMLButtonElement, ReturnType<typeof setTimeout>>()
|
||||||
|
|
||||||
@@ -111,62 +175,6 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) {
|
|||||||
setCopyState(button, labels, copied)
|
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 handleClick = async (event: MouseEvent) => {
|
||||||
const target = event.target
|
const target = event.target
|
||||||
if (!(target instanceof Element)) return
|
if (!(target instanceof Element)) return
|
||||||
@@ -186,11 +194,7 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) {
|
|||||||
timeouts.set(button, timeout)
|
timeouts.set(button, timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const blocks = Array.from(root.querySelectorAll("pre"))
|
decorate(root, labels)
|
||||||
for (const block of blocks) {
|
|
||||||
ensureWrapper(block)
|
|
||||||
}
|
|
||||||
markCodeLinks()
|
|
||||||
|
|
||||||
const buttons = Array.from(root.querySelectorAll('[data-slot="markdown-copy-button"]'))
|
const buttons = Array.from(root.querySelectorAll('[data-slot="markdown-copy-button"]'))
|
||||||
for (const button of buttons) {
|
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) {
|
function touch(key: string, value: Entry) {
|
||||||
cache.delete(key)
|
cache.delete(key)
|
||||||
cache.set(key, value)
|
cache.set(key, value)
|
||||||
@@ -284,23 +274,15 @@ export function Markdown(
|
|||||||
|
|
||||||
const temp = document.createElement("div")
|
const temp = document.createElement("div")
|
||||||
temp.innerHTML = content
|
temp.innerHTML = content
|
||||||
wrapCodeBlocks(temp)
|
decorate(temp, {
|
||||||
|
copy: i18n.t("ui.message.copy"),
|
||||||
|
copied: i18n.t("ui.message.copied"),
|
||||||
|
})
|
||||||
|
|
||||||
morphdom(container, temp, {
|
morphdom(container, temp, {
|
||||||
childrenOnly: true,
|
childrenOnly: true,
|
||||||
onBeforeElUpdated: (fromEl, toEl) => {
|
onBeforeElUpdated: (fromEl, toEl) => {
|
||||||
if (fromEl.isEqualNode(toEl)) return false
|
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
|
return true
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user