Timothy J. Baek hai 9 meses
pai
achega
3b370bbcb3

+ 1 - 1
src/lib/components/chat/Messages/MarkdownInlineTokens.svelte

@@ -1,8 +1,8 @@
 <script lang="ts">
 	import type { Token } from 'marked';
 	import { unescapeHtml } from '$lib/utils';
+	import { onMount } from 'svelte';
 	import Image from '$lib/components/common/Image.svelte';
-
 	export let id: string;
 	export let tokens: Token[];
 </script>

+ 97 - 126
src/lib/components/chat/Messages/MarkdownTokens.svelte

@@ -1,141 +1,112 @@
 <script lang="ts">
-	import { marked } from 'marked';
 	import type { Token } from 'marked';
 	import { revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
-
-	import { onMount, getContext, getAllContexts } from 'svelte';
-
-	const context = getAllContexts();
-	const i18n = getContext('i18n');
-
-	import Image from '$lib/components/common/Image.svelte';
 	import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
-
+	import { onMount } from '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;
 	};
-
-	const renderer = new marked.Renderer();
-	// For code blocks with simple backticks
-	renderer.codespan = (code) => {
-		return `<code class="codespan">${code.replaceAll('&amp;', '&')}</code>`;
-	};
-
-	let codes = [];
-	renderer.code = (code, lang) => {
-		codes.push({
-			code: code,
-			lang: lang
-		});
-		codes = codes;
-		const codeId = `${id}-${codes.length}`;
-
-		const interval = setInterval(() => {
-			const codeElement = document.getElementById(`code-${codeId}`);
-			if (codeElement) {
-				clearInterval(interval);
-				// If the code is already loaded, don't load it again
-				if (codeElement.innerHTML) {
-					return;
-				}
-
-				const element = new CodeBlock({
-					target: codeElement,
-					props: {
-						id: `${id}-${codes.length}`,
-						lang: lang,
-						code: revertSanitizedResponseContent(code)
-					},
-					hydrate: true,
-					$$inline: true,
-					context: context
-				});
-			}
-		}, 10);
-
-		return `<div id="code-${id}-${codes.length}"></div>`;
-	};
-
-	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(() => {
-			const imageElement = document.getElementById(`image-${imageId}`);
-			if (imageElement) {
-				clearInterval(interval);
-
-				// If the image is already loaded, don't load it again
-				if (imageElement.innerHTML) {
-					return;
-				}
-
-				console.log('image', href, text);
-				new Image({
-					target: imageElement,
-					props: {
-						src: href,
-						alt: text
-					},
-					$$inline: true
-				});
-			}
-		}, 10);
-
-		return `<div id="image-${id}-${images.length}"></div>`;
-	};
-
-	// Open all links in a new tab/window (from https://github.com/markedjs/marked/issues/655#issuecomment-383226346)
-	const origLinkRenderer = renderer.link;
-	renderer.link = (href, title, text) => {
-		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>
 
-<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}
+{#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}
+			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'}
+		{@html token.text}
+	{: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 ?? []} />
 		{:else}
-			{@html marked.parse(token.raw, {
-				...defaults,
-				gfm: true,
-				breaks: true,
-				renderer
-			})}
+			{unescapeHtml(token.text)}
 		{/if}
-	{/each}
-</div>
+	{:else if token.type === 'space'}
+		{''}
+	{:else}
+		{console.log('Unknown token', token)}
+	{/if}
+{/each}

+ 2 - 2
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -99,7 +99,7 @@
 			tooltipInstance[0]?.destroy();
 		}
 
-		renderLatex();
+		// renderLatex();
 
 		if (message.info) {
 			let tooltipContent = '';
@@ -420,7 +420,7 @@
 				{/if}
 
 				<div
-					class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-2 prose-ol:-my-2 prose-li:-my-3 whitespace-pre-line"
+					class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line"
 				>
 					<div>
 						{#if (message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length > 0}