|
<script lang="ts"> |
|
import DOMPurify from 'dompurify'; |
|
import { toast } from 'svelte-sonner'; |
|
|
|
import type { Token } from 'marked'; |
|
import { getContext } from 'svelte'; |
|
|
|
const i18n = getContext('i18n'); |
|
|
|
import { WEBUI_BASE_URL } from '$lib/constants'; |
|
import { copyToClipboard, revertSanitizedResponseContent, unescapeHtml } from '$lib/utils'; |
|
|
|
import Image from '$lib/components/common/Image.svelte'; |
|
import KatexRenderer from './KatexRenderer.svelte'; |
|
|
|
export let id: string; |
|
export let tokens: Token[]; |
|
</script> |
|
|
|
{#each tokens as token} |
|
{#if token.type === 'escape'} |
|
{unescapeHtml(token.text)} |
|
{:else if token.type === 'html'} |
|
{@const html = DOMPurify.sanitize(token.text)} |
|
{#if html && html.includes('<video')} |
|
{@html html} |
|
{:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)} |
|
{@html `${token.text}`} |
|
{:else} |
|
{token.text} |
|
{/if} |
|
{:else if token.type === 'link'} |
|
{#if token.tokens} |
|
<a href={token.href} target="_blank" rel="nofollow" title={token.title}> |
|
<svelte:self id={`${id}-a`} tokens={token.tokens} /> |
|
</a> |
|
{:else} |
|
<a href={token.href} target="_blank" rel="nofollow" title={token.title}>{token.text}</a> |
|
{/if} |
|
{:else if token.type === 'image'} |
|
<Image src={token.href} alt={token.text} /> |
|
{:else if token.type === 'strong'} |
|
<strong> |
|
<svelte:self id={`${id}-strong`} tokens={token.tokens} /> |
|
</strong> |
|
{:else if token.type === 'em'} |
|
<em> |
|
<svelte:self id={`${id}-em`} tokens={token.tokens} /> |
|
</em> |
|
{:else if token.type === 'codespan'} |
|
|
|
|
|
<code |
|
class="codespan cursor-pointer" |
|
on:click={() => { |
|
copyToClipboard(unescapeHtml(token.text)); |
|
toast.success($i18n.t('Copied to clipboard')); |
|
}}>{unescapeHtml(token.text)}</code |
|
> |
|
{:else if token.type === 'br'} |
|
<br /> |
|
{:else if token.type === 'del'} |
|
<del> |
|
<svelte:self id={`${id}-del`} tokens={token.tokens} /> |
|
</del> |
|
{:else if token.type === 'inlineKatex'} |
|
{#if token.text} |
|
<KatexRenderer content={revertSanitizedResponseContent(token.text)} displayMode={false} /> |
|
{/if} |
|
{:else if token.type === 'iframe'} |
|
<iframe |
|
src="{WEBUI_BASE_URL}/api/v1/files/{token.fileId}/content" |
|
title={token.fileId} |
|
width="100%" |
|
frameborder="0" |
|
onload="this.style.height=(this.contentWindow.document.body.scrollHeight+20)+'px';" |
|
></iframe> |
|
{:else if token.type === 'text'} |
|
{token.raw} |
|
{/if} |
|
{/each} |
|
|