Explorar el Código

fix(ui): support cmd-click links in inline code (#12552)

Kit Langton hace 1 día
padre
commit
d30e917385

+ 5 - 0
packages/ui/src/components/markdown.css

@@ -209,3 +209,8 @@
     display: block;
   }
 }
+
+[data-component="markdown"] a.external-link:hover > code {
+  text-decoration: underline;
+  text-underline-offset: 2px;
+}

+ 44 - 0
packages/ui/src/components/markdown.tsx

@@ -49,6 +49,19 @@ type CopyLabels = {
   copied: string
 }
 
+const urlPattern = /^https?:\/\/[^\s<>()`"']+$/
+
+function codeUrl(text: string) {
+  const href = text.trim().replace(/[),.;!?]+$/, "")
+  if (!urlPattern.test(href)) return
+  try {
+    const url = new URL(href)
+    return url.toString()
+  } catch {
+    return
+  }
+}
+
 function createIcon(path: string, slot: string) {
   const icon = document.createElement("div")
   icon.setAttribute("data-component", "icon")
@@ -110,9 +123,39 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) {
     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 target = event.target
     if (!(target instanceof Element)) return
+
     const button = target.closest('[data-slot="markdown-copy-button"]')
     if (!(button instanceof HTMLButtonElement)) return
     const code = button.closest('[data-component="markdown-code"]')?.querySelector("code")
@@ -132,6 +175,7 @@ function setupCodeCopy(root: HTMLDivElement, labels: CopyLabels) {
   for (const block of blocks) {
     ensureWrapper(block)
   }
+  markCodeLinks()
 
   const buttons = Array.from(root.querySelectorAll('[data-slot="markdown-copy-button"]'))
   for (const button of buttons) {