2024-08-05 15:47:18 +00:00
|
|
|
<script lang="ts">
|
2024-08-14 22:08:15 +00:00
|
|
|
import DOMPurify from 'dompurify';
|
2024-08-08 22:01:38 +00:00
|
|
|
import { onMount } from 'svelte';
|
2024-08-16 15:51:50 +00:00
|
|
|
import { marked, type Token } from 'marked';
|
2024-08-06 09:43:47 +00:00
|
|
|
import { revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
|
2024-08-08 22:01:38 +00:00
|
|
|
|
2024-08-06 23:55:37 +00:00
|
|
|
import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
|
2024-08-18 16:28:58 +00:00
|
|
|
import MarkdownInlineTokens from '$lib/components/chat/Messages/Markdown/MarkdownInlineTokens.svelte';
|
2024-08-08 22:01:38 +00:00
|
|
|
import KatexRenderer from './KatexRenderer.svelte';
|
2024-08-16 13:33:14 +00:00
|
|
|
import { WEBUI_BASE_URL } from '$lib/constants';
|
2024-08-08 22:01:38 +00:00
|
|
|
|
2024-08-05 15:47:18 +00:00
|
|
|
export let id: string;
|
|
|
|
export let tokens: Token[];
|
|
|
|
export let top = true;
|
2024-08-08 22:01:38 +00:00
|
|
|
|
2024-08-05 15:47:18 +00:00
|
|
|
const headerComponent = (depth: number) => {
|
|
|
|
return 'h' + depth;
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
2024-08-08 22:01:38 +00:00
|
|
|
<!-- {JSON.stringify(tokens)} -->
|
2024-08-08 18:46:39 +00:00
|
|
|
{#each tokens as token, tokenIdx}
|
|
|
|
{#if token.type === 'hr'}
|
|
|
|
<hr />
|
|
|
|
{:else if token.type === 'heading'}
|
|
|
|
<svelte:element this={headerComponent(token.depth)}>
|
|
|
|
<MarkdownInlineTokens id={`${id}-${tokenIdx}-h`} tokens={token.tokens} />
|
|
|
|
</svelte:element>
|
|
|
|
{:else if token.type === 'code'}
|
|
|
|
<CodeBlock
|
2024-08-14 19:16:07 +00:00
|
|
|
id={`${id}-${tokenIdx}`}
|
2024-08-14 15:34:44 +00:00
|
|
|
{token}
|
2024-08-08 18:46:39 +00:00
|
|
|
lang={token?.lang ?? ''}
|
|
|
|
code={revertSanitizedResponseContent(token?.text ?? '')}
|
|
|
|
/>
|
|
|
|
{:else if token.type === 'table'}
|
|
|
|
<table>
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
{#each token.header as header, headerIdx}
|
|
|
|
<th style={token.align[headerIdx] ? '' : `text-align: ${token.align[headerIdx]}`}>
|
|
|
|
<MarkdownInlineTokens
|
|
|
|
id={`${id}-${tokenIdx}-header-${headerIdx}`}
|
|
|
|
tokens={header.tokens}
|
|
|
|
/>
|
|
|
|
</th>
|
|
|
|
{/each}
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
{#each token.rows as row, rowIdx}
|
|
|
|
<tr>
|
|
|
|
{#each row ?? [] as cell, cellIdx}
|
|
|
|
<td style={token.align[cellIdx] ? '' : `text-align: ${token.align[cellIdx]}`}>
|
|
|
|
<MarkdownInlineTokens
|
|
|
|
id={`${id}-${tokenIdx}-row-${rowIdx}-${cellIdx}`}
|
|
|
|
tokens={cell.tokens}
|
|
|
|
/>
|
|
|
|
</td>
|
|
|
|
{/each}
|
|
|
|
</tr>
|
|
|
|
{/each}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
{:else if token.type === 'blockquote'}
|
|
|
|
<blockquote>
|
|
|
|
<svelte:self id={`${id}-${tokenIdx}`} tokens={token.tokens} />
|
|
|
|
</blockquote>
|
|
|
|
{:else if token.type === 'list'}
|
|
|
|
{#if token.ordered}
|
|
|
|
<ol start={token.start || 1}>
|
|
|
|
{#each token.items as item, itemIdx}
|
|
|
|
<li>
|
|
|
|
<svelte:self
|
|
|
|
id={`${id}-${tokenIdx}-${itemIdx}`}
|
|
|
|
tokens={item.tokens}
|
|
|
|
top={token.loose}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
{/each}
|
|
|
|
</ol>
|
|
|
|
{:else}
|
|
|
|
<ul>
|
|
|
|
{#each token.items as item, itemIdx}
|
|
|
|
<li>
|
|
|
|
<svelte:self
|
|
|
|
id={`${id}-${tokenIdx}-${itemIdx}`}
|
|
|
|
tokens={item.tokens}
|
|
|
|
top={token.loose}
|
|
|
|
/>
|
|
|
|
</li>
|
|
|
|
{/each}
|
|
|
|
</ul>
|
|
|
|
{/if}
|
|
|
|
{:else if token.type === 'html'}
|
2024-08-14 22:08:15 +00:00
|
|
|
{@const html = DOMPurify.sanitize(token.text)}
|
2024-08-16 13:44:18 +00:00
|
|
|
{#if html && html.includes('<video')}
|
2024-08-14 22:08:15 +00:00
|
|
|
{@html html}
|
2024-08-16 15:51:50 +00:00
|
|
|
{:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)}
|
|
|
|
{@html `${token.text}`}
|
2024-08-14 22:08:15 +00:00
|
|
|
{:else}
|
|
|
|
{token.text}
|
|
|
|
{/if}
|
2024-08-16 13:33:14 +00:00
|
|
|
{: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>
|
2024-08-08 18:46:39 +00:00
|
|
|
{:else if token.type === 'paragraph'}
|
|
|
|
<p>
|
|
|
|
<MarkdownInlineTokens id={`${id}-${tokenIdx}-p`} tokens={token.tokens ?? []} />
|
|
|
|
</p>
|
|
|
|
{:else if token.type === 'text'}
|
|
|
|
{#if top}
|
|
|
|
<p>
|
|
|
|
{#if token.tokens}
|
|
|
|
<MarkdownInlineTokens id={`${id}-${tokenIdx}-t`} tokens={token.tokens} />
|
|
|
|
{:else}
|
|
|
|
{unescapeHtml(token.text)}
|
|
|
|
{/if}
|
|
|
|
</p>
|
|
|
|
{:else if token.tokens}
|
|
|
|
<MarkdownInlineTokens id={`${id}-${tokenIdx}-p`} tokens={token.tokens ?? []} />
|
2024-08-06 23:55:37 +00:00
|
|
|
{:else}
|
2024-08-08 18:46:39 +00:00
|
|
|
{unescapeHtml(token.text)}
|
2024-08-06 23:55:37 +00:00
|
|
|
{/if}
|
2024-08-08 22:01:38 +00:00
|
|
|
{:else if token.type === 'inlineKatex'}
|
|
|
|
{#if token.text}
|
|
|
|
<KatexRenderer
|
|
|
|
content={revertSanitizedResponseContent(token.text)}
|
|
|
|
displayMode={token?.displayMode ?? false}
|
|
|
|
/>
|
|
|
|
{/if}
|
2024-08-14 14:07:39 +00:00
|
|
|
{:else if token.type === 'blockKatex'}
|
|
|
|
{#if token.text}
|
|
|
|
<KatexRenderer
|
|
|
|
content={revertSanitizedResponseContent(token.text)}
|
|
|
|
displayMode={token?.displayMode ?? false}
|
|
|
|
/>
|
|
|
|
{/if}
|
2024-08-08 18:46:39 +00:00
|
|
|
{:else if token.type === 'space'}
|
|
|
|
{''}
|
|
|
|
{:else}
|
|
|
|
{console.log('Unknown token', token)}
|
|
|
|
{/if}
|
|
|
|
{/each}
|