Ver Fonte

refac: response message

Timothy J. Baek há 1 ano atrás
pai
commit
e758855590

+ 36 - 0
src/lib/components/chat/Messages/CodeBlock.svelte

@@ -0,0 +1,36 @@
+<script lang="ts">
+	import { copyToClipboard } from '$lib/utils';
+	import hljs from 'highlight.js';
+	import 'highlight.js/styles/github-dark.min.css';
+
+	export let lang = '';
+	export let code = '';
+
+	let copied = false;
+
+	const copyCode = async () => {
+		copied = true;
+		await copyToClipboard(code);
+
+		setTimeout(() => {
+			copied = false;
+		}, 1000);
+	};
+
+	$: highlightedCode = code ? hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value : '';
+</script>
+
+<div class="mb-3">
+	<div
+		class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 rounded-t-lg overflow-x-auto"
+	>
+		<div class="p-1">{lang}</div>
+		<button class="copy-code-button bg-none border-none p-1" on:click={copyCode}
+			>{copied ? 'Copied' : 'Copy Code'}</button
+		>
+	</div>
+
+	<pre class=" rounded-b-lg hljs p-4 overflow-x-auto rounded-t-none"><code
+			class="language-{lang} rounded-t-none whitespace-pre">{@html highlightedCode || code}</code
+		></pre>
+</div>

+ 31 - 72
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -1,17 +1,16 @@
 <script lang="ts">
 	import dayjs from 'dayjs';
 	import { marked } from 'marked';
-
 	import tippy from 'tippy.js';
-	import hljs from 'highlight.js';
-	import 'highlight.js/styles/github-dark.min.css';
 	import auto_render from 'katex/dist/contrib/auto-render.mjs';
 	import 'katex/dist/katex.min.css';
 
+	import { onMount, tick } from 'svelte';
+
 	import Name from './Name.svelte';
 	import ProfileImage from './ProfileImage.svelte';
 	import Skeleton from './Skeleton.svelte';
-	import { onMount, tick } from 'svelte';
+	import CodeBlock from './CodeBlock.svelte';
 
 	export let modelfiles = [];
 	export let message;
@@ -33,6 +32,20 @@
 	let tooltipInstance = null;
 	let speaking = null;
 
+	$: tokens = marked.lexer(message.content);
+
+	const renderer = new marked.Renderer();
+
+	// For code blocks with simple backticks
+	renderer.codespan = (code) => {
+		return `<code>${code.replaceAll('&amp;', '&')}</code>`;
+	};
+
+	const { extensions, ...defaults } = marked.getDefaults() as marked.MarkedOptions & {
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
+		extensions: any;
+	};
+
 	$: if (message) {
 		renderStyling();
 	}
@@ -45,8 +58,6 @@
 		}
 
 		renderLatex();
-		hljs.highlightAll();
-		createCopyCodeBlockButton();
 
 		if (message.info) {
 			tooltipInstance = tippy(`#info-${message.id}`, {
@@ -78,71 +89,6 @@
 		}
 	};
 
-	const createCopyCodeBlockButton = () => {
-		// use a class selector if available
-		let blocks = document.querySelectorAll('pre');
-
-		blocks.forEach((block) => {
-			// only add button if browser supports Clipboard API
-
-			if (block.childNodes.length < 2 && block.id !== 'user-message') {
-				let code = block.querySelector('code');
-				code.style.borderTopRightRadius = 0;
-				code.style.borderTopLeftRadius = 0;
-				code.style.whiteSpace = 'pre';
-
-				let topBarDiv = document.createElement('div');
-				topBarDiv.style.backgroundColor = '#202123';
-				topBarDiv.style.overflowX = 'auto';
-				topBarDiv.style.display = 'flex';
-				topBarDiv.style.justifyContent = 'space-between';
-				topBarDiv.style.padding = '0 1rem';
-				topBarDiv.style.paddingTop = '4px';
-				topBarDiv.style.borderTopRightRadius = '8px';
-				topBarDiv.style.borderTopLeftRadius = '8px';
-
-				let langDiv = document.createElement('div');
-
-				let codeClassNames = code?.className.split(' ');
-				langDiv.textContent =
-					codeClassNames[0] === 'hljs' ? codeClassNames[1].slice(9) : codeClassNames[0].slice(9);
-				langDiv.style.color = 'white';
-				langDiv.style.margin = '4px';
-				langDiv.style.fontSize = '0.75rem';
-
-				let button = document.createElement('button');
-				button.className = 'copy-code-button';
-				button.textContent = 'Copy Code';
-				button.style.background = 'none';
-				button.style.fontSize = '0.75rem';
-				button.style.border = 'none';
-				button.style.margin = '4px';
-				button.style.cursor = 'pointer';
-				button.style.color = '#ddd';
-				button.addEventListener('click', () => copyCode(block, button));
-
-				topBarDiv.appendChild(langDiv);
-				topBarDiv.appendChild(button);
-
-				block.prepend(topBarDiv);
-			}
-		});
-
-		async function copyCode(block, button) {
-			let code = block.querySelector('code');
-			let text = code.innerText;
-
-			await copyToClipboard(text);
-
-			// visual feedback that task is completed
-			button.innerText = 'Copied!';
-
-			setTimeout(() => {
-				button.innerText = 'Copy Code';
-			}, 1000);
-		}
-	};
-
 	const renderLatex = () => {
 		let chatMessageElements = document.getElementsByClassName('chat-assistant');
 		// let lastChatMessageElement = chatMessageElements[chatMessageElements.length - 1];
@@ -292,7 +238,20 @@
 									</div>
 								</div>
 							{:else}
-								{@html marked(message.content.replaceAll('\\', '\\\\'))}
+								{#each tokens as token}
+									{#if token.type === 'code'}
+										<CodeBlock lang={token.lang} code={token.text} />
+									{:else}
+										<!-- eslint-disable-next-line svelte/no-at-html-tags -->
+										{@html marked.parse(token.raw, {
+											...defaults,
+											gfm: true,
+											breaks: true,
+											renderer
+										})}
+									{/if}
+								{/each}
+								<!-- {@html marked(message.content.replaceAll('\\', '\\\\'))} -->
 							{/if}
 
 							{#if message.done}