Files
ComfyUI_frontend/src/utils/markdownRendererUtil.ts
Christian Byrne 89ff8255bd open markdown links in new window/tab (#6229)
## Summary

Changes links in markdown snippets (What's New popup, node info sidebar)
to open the link in a new tab/window rather than directly navigating and
potentially losing unsaved work.



https://github.com/user-attachments/assets/24331bba-e31a-484c-bc11-12cf61805c98



Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/6223.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6229-open-markdown-links-in-new-window-tab-2956d73d365081edbb1efb21cd0e2ab2)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: GitHub Action <action@github.com>
2025-10-23 13:33:38 -07:00

61 lines
1.9 KiB
TypeScript

import { default as DOMPurify } from 'dompurify'
import { Renderer, marked } from 'marked'
const ALLOWED_TAGS = ['video', 'source']
const ALLOWED_ATTRS = [
'controls',
'autoplay',
'loop',
'muted',
'preload',
'poster'
]
// Matches relative src attributes in img, source, and video HTML tags
// Captures: 1) opening tag with src=", 2) relative path, 3) closing quote
// Excludes absolute paths (starting with /) and URLs (http:// or https://)
const MEDIA_SRC_REGEX =
/(<(?:img|source|video)[^>]*\ssrc=['"])(?!(?:\/|https?:\/\/))([^'"\s>]+)(['"])/gi
// Create a marked Renderer that prefixes relative URLs with base
export function createMarkdownRenderer(baseUrl?: string): Renderer {
const normalizedBase = baseUrl ? baseUrl.replace(/\/+$/, '') : ''
const renderer = new Renderer()
renderer.image = ({ href, title, text }) => {
let src = href
if (normalizedBase && !/^(?:\/|https?:\/\/)/.test(href)) {
src = `${normalizedBase}/${href}`
}
const titleAttr = title ? ` title="${title}"` : ''
return `<img src="${src}" alt="${text}"${titleAttr} />`
}
renderer.link = ({ href, title, tokens, text }) => {
// For autolinks (bare URLs), tokens may be undefined, so fall back to text
const linkText = tokens ? renderer.parser.parseInline(tokens) : text
const titleAttr = title ? ` title="${title}"` : ''
return `<a href="${href}" ${titleAttr} target="_blank" rel="noopener noreferrer">${linkText}</a>`
}
return renderer
}
export function renderMarkdownToHtml(
markdown: string,
baseUrl?: string
): string {
if (!markdown) return ''
let html = marked.parse(markdown, {
renderer: createMarkdownRenderer(baseUrl),
gfm: true // Enable GitHub Flavored Markdown (including autolinks)
}) as string
if (baseUrl) {
html = html.replace(MEDIA_SRC_REGEX, `$1${baseUrl}$2$3`)
}
return DOMPurify.sanitize(html, {
ADD_TAGS: ALLOWED_TAGS,
ADD_ATTR: [...ALLOWED_ATTRS, 'target', 'rel']
})
}