mirror of
https://github.com/open-webui/open-webui
synced 2025-01-29 13:58:09 +00:00
refac: token rendering
This commit is contained in:
parent
05bbca5b07
commit
7d42a79177
30
src/lib/components/chat/Messages/HTMLRenderer.svelte
Normal file
30
src/lib/components/chat/Messages/HTMLRenderer.svelte
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Image from '$lib/components/common/Image.svelte';
|
||||||
|
import CodeBlock from './CodeBlock.svelte';
|
||||||
|
|
||||||
|
/* The html content of the tag */
|
||||||
|
export let html; //: string;
|
||||||
|
let parsedHTML = [html];
|
||||||
|
|
||||||
|
export let images;
|
||||||
|
export let codes;
|
||||||
|
|
||||||
|
// all images are in {{IMAGE_0}}, {{IMAGE_1}}.... format
|
||||||
|
// all codes are in {{CODE_0}}, {{CODE_1}}.... format
|
||||||
|
|
||||||
|
const rules = [];
|
||||||
|
rules.forEach((rule) => {
|
||||||
|
parsedHTML = parsedHTML.map((substr) => substr.split(rule.regex)).flat();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each parsedHTML as part}
|
||||||
|
{@const match = rules.find((rule) => rule.regex.test(part))}
|
||||||
|
{#if match}
|
||||||
|
<svelte:component this={match.component} {...match.props}>
|
||||||
|
{@html part}
|
||||||
|
</svelte:component>
|
||||||
|
{:else}
|
||||||
|
{@html part}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
@ -10,6 +10,9 @@
|
|||||||
export let tokenIdx = 0;
|
export let tokenIdx = 0;
|
||||||
export let id;
|
export let id;
|
||||||
|
|
||||||
|
let element;
|
||||||
|
let content;
|
||||||
|
|
||||||
const renderer = new marked.Renderer();
|
const renderer = new marked.Renderer();
|
||||||
|
|
||||||
// For code blocks with simple backticks
|
// For code blocks with simple backticks
|
||||||
@ -17,30 +20,19 @@
|
|||||||
return `<code>${code.replaceAll('&', '&')}</code>`;
|
return `<code>${code.replaceAll('&', '&')}</code>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// renderer.code = (code, lang) => {
|
let codes = [];
|
||||||
// const element = document.createElement('div');
|
renderer.code = (code, lang) => {
|
||||||
// new CodeBlock({
|
codes.push({ code, lang, id: codes.length });
|
||||||
// target: element,
|
codes = codes;
|
||||||
// props: {
|
return `{{@CODE ${codes.length - 1}}}`;
|
||||||
// id: `${id}-${tokenIdx}`,
|
};
|
||||||
// lang: lang ?? '',
|
|
||||||
// code: revertSanitizedResponseContent(code ?? '')
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// return element.innerHTML;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// renderer.image = (href, title, text) => {
|
let images = [];
|
||||||
// const element = document.createElement('pre');
|
renderer.image = (href, title, text) => {
|
||||||
// new Image({
|
images.push({ href, title, text });
|
||||||
// target: element,
|
images = images;
|
||||||
// props: {
|
return `{{@IMAGE ${images.length - 1}}}`;
|
||||||
// src: href,
|
};
|
||||||
// alt: text
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// return element.innerHTML;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// Open all links in a new tab/window (from https://github.com/markedjs/marked/issues/655#issuecomment-383226346)
|
// Open all links in a new tab/window (from https://github.com/markedjs/marked/issues/655#issuecomment-383226346)
|
||||||
const origLinkRenderer = renderer.link;
|
const origLinkRenderer = renderer.link;
|
||||||
@ -53,23 +45,50 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
extensions: any;
|
extensions: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$: if (token) {
|
||||||
|
images = [];
|
||||||
|
codes = [];
|
||||||
|
content = marked
|
||||||
|
.parse(token.raw, {
|
||||||
|
...defaults,
|
||||||
|
gfm: true,
|
||||||
|
breaks: true,
|
||||||
|
renderer
|
||||||
|
})
|
||||||
|
.split(/({{@IMAGE [^}]+}}|{{@CODE [^}]+}})/g);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if token.type === 'code'}
|
<div bind:this={element}>
|
||||||
{#if token.lang === 'mermaid'}
|
{#if token.type === 'code'}
|
||||||
<pre class="mermaid">{revertSanitizedResponseContent(token.text)}</pre>
|
{#if token.lang === 'mermaid'}
|
||||||
|
<pre class="mermaid">{revertSanitizedResponseContent(token.text)}</pre>
|
||||||
|
{:else}
|
||||||
|
<CodeBlock
|
||||||
|
id={`${id}-${tokenIdx}`}
|
||||||
|
lang={token?.lang ?? ''}
|
||||||
|
code={revertSanitizedResponseContent(token?.text ?? '')}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{:else if token.type === 'image'}
|
||||||
|
<Image src={token.href} alt={token.text} />
|
||||||
{:else}
|
{:else}
|
||||||
<CodeBlock
|
{#each content as part}
|
||||||
id={`${id}-${tokenIdx}`}
|
{@html part.startsWith('{{@IMAGE ') || part.startsWith('{{@CODE ') ? '' : part}
|
||||||
lang={token?.lang ?? ''}
|
|
||||||
code={revertSanitizedResponseContent(token?.text ?? '')}
|
{#if images.length > 0 && part.startsWith('{{@IMAGE ')}
|
||||||
/>
|
{@const img = images[parseInt(part.match(/{{@IMAGE (\d+)}}/)[1])]}
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<Image src={img.href} text={img.text} />
|
||||||
|
</div>
|
||||||
|
{:else if codes.length > 0 && part.startsWith('{{@CODE ')}
|
||||||
|
{@const _code = codes[parseInt(part.match(/{{@CODE (\d+)}}/)[1])]}
|
||||||
|
<div class="my-10 -mb-6">
|
||||||
|
<CodeBlock id={`${id}-${tokenIdx}-${_code.id}`} lang={_code.lang} code={_code.code} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
</div>
|
||||||
{@html marked.parse(token.raw, {
|
|
||||||
...defaults,
|
|
||||||
gfm: true,
|
|
||||||
breaks: true,
|
|
||||||
renderer
|
|
||||||
})}
|
|
||||||
{/if}
|
|
||||||
|
@ -12,14 +12,14 @@
|
|||||||
let showImagePreview = false;
|
let showImagePreview = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full py-3">
|
<div>
|
||||||
<button
|
<button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
showImagePreview = true;
|
showImagePreview = true;
|
||||||
}}
|
}}
|
||||||
|
class="w-full"
|
||||||
>
|
>
|
||||||
<img src={_src} {alt} class=" max-h-96 rounded-lg" draggable="false" data-cy="image" />
|
<img src={_src} {alt} class=" max-h-96 rounded-lg" draggable="false" data-cy="image" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ImagePreview bind:show={showImagePreview} src={_src} {alt} />
|
<ImagePreview bind:show={showImagePreview} src={_src} {alt} />
|
||||||
|
Loading…
Reference in New Issue
Block a user