open-webui/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte

150 lines
4.0 KiB
Svelte
Raw Normal View History

<script lang="ts">
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';
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
export let id: string;
export let tokens: Token[];
export let top = true;
2024-08-08 22:01:38 +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
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'}
{@const html = DOMPurify.sanitize(token.text)}
2024-08-16 13:44:18 +00:00
{#if html && html.includes('<video')}
{@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}`}
{: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}