web: open links in chat in new tab
This commit is contained in:
@@ -478,6 +478,7 @@ export function ChatMessagesPanel({ messages, isLoading, isSending }: Props) {
|
|||||||
) : message.content.trim() ? (
|
) : message.content.trim() ? (
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
markdown={message.content}
|
markdown={message.content}
|
||||||
|
openLinksInNewTab
|
||||||
className={cn("[&_a]:text-inherit [&_a]:underline", isUser ? "leading-[1.78] text-fuchsia-50" : "leading-[1.82] text-violet-50")}
|
className={cn("[&_a]:text-inherit [&_a]:underline", isUser ? "leading-[1.78] text-fuchsia-50" : "leading-[1.82] text-violet-50")}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type Props = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
mode?: MarkdownMode;
|
mode?: MarkdownMode;
|
||||||
resolveCitationIndex?: (href: string) => number | undefined;
|
resolveCitationIndex?: (href: string) => number | undefined;
|
||||||
|
openLinksInNewTab?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function replaceMarkdownLinksWithCitationTokens(markdown: string, resolveCitationIndex?: (href: string) => number | undefined) {
|
function replaceMarkdownLinksWithCitationTokens(markdown: string, resolveCitationIndex?: (href: string) => number | undefined) {
|
||||||
@@ -28,17 +29,30 @@ markdownRenderer.table = (token) => {
|
|||||||
return `<div class="md-table-scroll">${renderTable(token)}</div>`;
|
return `<div class="md-table-scroll">${renderTable(token)}</div>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderMarkdown(markdown: string) {
|
function setNewTabLinkAttributes(currentNode: Element) {
|
||||||
const rawHtml = marked.parse(markdown, { gfm: true, breaks: true, renderer: markdownRenderer }) as string;
|
if (currentNode.tagName !== "A") return;
|
||||||
return DOMPurify.sanitize(rawHtml, { ADD_ATTR: ["class", "target", "rel"] });
|
currentNode.setAttribute("target", "_blank");
|
||||||
|
currentNode.setAttribute("rel", "noopener noreferrer");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MarkdownContent({ markdown, className, mode = "default", resolveCitationIndex }: Props) {
|
function renderMarkdown(markdown: string, openLinksInNewTab: boolean) {
|
||||||
|
const rawHtml = marked.parse(markdown, { gfm: true, breaks: true, renderer: markdownRenderer }) as string;
|
||||||
|
if (!openLinksInNewTab) return DOMPurify.sanitize(rawHtml, { ADD_ATTR: ["class", "target", "rel"] });
|
||||||
|
|
||||||
|
DOMPurify.addHook("afterSanitizeAttributes", setNewTabLinkAttributes);
|
||||||
|
try {
|
||||||
|
return DOMPurify.sanitize(rawHtml, { ADD_ATTR: ["class", "target", "rel"] });
|
||||||
|
} finally {
|
||||||
|
DOMPurify.removeHook("afterSanitizeAttributes", setNewTabLinkAttributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarkdownContent({ markdown, className, mode = "default", resolveCitationIndex, openLinksInNewTab = false }: Props) {
|
||||||
const html = useMemo(() => {
|
const html = useMemo(() => {
|
||||||
const prepared =
|
const prepared =
|
||||||
mode === "citationTokens" ? replaceMarkdownLinksWithCitationTokens(markdown, resolveCitationIndex) : markdown;
|
mode === "citationTokens" ? replaceMarkdownLinksWithCitationTokens(markdown, resolveCitationIndex) : markdown;
|
||||||
return renderMarkdown(prepared);
|
return renderMarkdown(prepared, openLinksInNewTab);
|
||||||
}, [markdown, mode, resolveCitationIndex]);
|
}, [markdown, mode, openLinksInNewTab, resolveCitationIndex]);
|
||||||
|
|
||||||
return <div className={cn("md-content", className)} dangerouslySetInnerHTML={{ __html: html }} />;
|
return <div className={cn("md-content", className)} dangerouslySetInnerHTML={{ __html: html }} />;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user