|
@@ -2,14 +2,20 @@
|
|
|
import { marked } from 'marked';
|
|
|
import type { Token } from 'marked';
|
|
|
import { revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
|
|
|
- import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
|
|
|
+
|
|
|
import { onMount } from 'svelte';
|
|
|
+
|
|
|
+ import Image from '$lib/components/common/Image.svelte';
|
|
|
+ import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
|
|
|
+
|
|
|
import MarkdownInlineTokens from '$lib/components/chat/Messages/MarkdownInlineTokens.svelte';
|
|
|
|
|
|
export let id: string;
|
|
|
export let tokens: Token[];
|
|
|
export let top = true;
|
|
|
|
|
|
+ let containerElement;
|
|
|
+
|
|
|
const headerComponent = (depth: number) => {
|
|
|
return 'h' + depth;
|
|
|
};
|
|
@@ -20,8 +26,61 @@
|
|
|
return `<code class="codespan">${code.replaceAll('&', '&')}</code>`;
|
|
|
};
|
|
|
|
|
|
+ let codes = [];
|
|
|
renderer.code = (code, lang) => {
|
|
|
- return `<pre><code class="language-${lang}">${code}</code></pre>`;
|
|
|
+ codes.push({
|
|
|
+ code: code,
|
|
|
+ lang: lang
|
|
|
+ });
|
|
|
+ codes = codes;
|
|
|
+ const codeId = `${id}-${codes.length}`;
|
|
|
+
|
|
|
+ const interval = setInterval(() => {
|
|
|
+ if (document.getElementById(`code-${codeId}`)) {
|
|
|
+ clearInterval(interval);
|
|
|
+
|
|
|
+ new CodeBlock({
|
|
|
+ target: document.getElementById(`code-${codeId}`),
|
|
|
+ props: {
|
|
|
+ id: `${id}-${codes.length}`,
|
|
|
+ lang: lang,
|
|
|
+ code: revertSanitizedResponseContent(code)
|
|
|
+ },
|
|
|
+ hydrate: true,
|
|
|
+ $$inline: true
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }, 10);
|
|
|
+
|
|
|
+ return `<div id="code-${id}-${codes.length}" />`;
|
|
|
+ };
|
|
|
+
|
|
|
+ let images = [];
|
|
|
+ renderer.image = (href, title, text) => {
|
|
|
+ images.push({
|
|
|
+ href: href,
|
|
|
+ title: title,
|
|
|
+ text: text
|
|
|
+ });
|
|
|
+ images = images;
|
|
|
+
|
|
|
+ const imageId = `${id}-${images.length}`;
|
|
|
+
|
|
|
+ const interval = setInterval(() => {
|
|
|
+ if (document.getElementById(`image-${imageId}`)) {
|
|
|
+ clearInterval(interval);
|
|
|
+ new Image({
|
|
|
+ target: document.getElementById(`image-${imageId}`),
|
|
|
+ props: {
|
|
|
+ src: href,
|
|
|
+ alt: text
|
|
|
+ },
|
|
|
+ hydrate: true
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }, 10);
|
|
|
+
|
|
|
+ return `<div id="image-${id}-${images.length}" />`;
|
|
|
};
|
|
|
|
|
|
// Open all links in a new tab/window (from https://github.com/markedjs/marked/issues/655#issuecomment-383226346)
|
|
@@ -30,24 +89,31 @@
|
|
|
const html = origLinkRenderer.call(renderer, href, title, text);
|
|
|
return html.replace(/^<a /, '<a target="_blank" rel="nofollow" ');
|
|
|
};
|
|
|
+
|
|
|
const { extensions, ...defaults } = marked.getDefaults() as marked.MarkedOptions & {
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
extensions: any;
|
|
|
};
|
|
|
+
|
|
|
+ $: if (tokens) {
|
|
|
+ images = [];
|
|
|
+ codes = [];
|
|
|
+ }
|
|
|
</script>
|
|
|
|
|
|
-{#each tokens as token, tokenIdx (`${id}-${tokenIdx}`)}
|
|
|
- {#if token.type === 'code'}
|
|
|
- {#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 === 'heading'}
|
|
|
+<div bind:this={containerElement} class="flex flex-col">
|
|
|
+ {#each tokens as token, tokenIdx (`${id}-${tokenIdx}`)}
|
|
|
+ {#if token.type === 'code'}
|
|
|
+ {#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 === 'heading'}
|
|
|
<svelte:element this={headerComponent(token.depth)}>
|
|
|
<MarkdownInlineTokens id={`${id}-${tokenIdx}-h`} tokens={token.tokens} />
|
|
|
</svelte:element>
|
|
@@ -119,7 +185,7 @@
|
|
|
</tbody>
|
|
|
</table>
|
|
|
{:else if token.type === 'text'} -->
|
|
|
- <!-- {#if top}
|
|
|
+ <!-- {#if top}
|
|
|
<p>
|
|
|
{#if token.tokens}
|
|
|
<MarkdownInlineTokens id={`${id}-${tokenIdx}-t`} tokens={token.tokens} />
|
|
@@ -132,12 +198,13 @@
|
|
|
{:else}
|
|
|
{unescapeHtml(token.text)}
|
|
|
{/if} -->
|
|
|
- {:else}
|
|
|
- {@html marked.parse(token.raw, {
|
|
|
- ...defaults,
|
|
|
- gfm: true,
|
|
|
- breaks: true,
|
|
|
- renderer
|
|
|
- })}
|
|
|
- {/if}
|
|
|
-{/each}
|
|
|
+ {:else}
|
|
|
+ {@html marked.parse(token.raw, {
|
|
|
+ ...defaults,
|
|
|
+ gfm: true,
|
|
|
+ breaks: true,
|
|
|
+ renderer
|
|
|
+ })}
|
|
|
+ {/if}
|
|
|
+ {/each}
|
|
|
+</div>
|