forked from open-webui/open-webui
		
	refac: response message
This commit is contained in:
		
							parent
							
								
									1abe5a5487
								
							
						
					
					
						commit
						e758855590
					
				
					 2 changed files with 67 additions and 72 deletions
				
			
		
							
								
								
									
										36
									
								
								src/lib/components/chat/Messages/CodeBlock.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/lib/components/chat/Messages/CodeBlock.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -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> | ||||||
|  | @ -1,17 +1,16 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import dayjs from 'dayjs'; | 	import dayjs from 'dayjs'; | ||||||
| 	import { marked } from 'marked'; | 	import { marked } from 'marked'; | ||||||
| 
 |  | ||||||
| 	import tippy from 'tippy.js'; | 	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 auto_render from 'katex/dist/contrib/auto-render.mjs'; | ||||||
| 	import 'katex/dist/katex.min.css'; | 	import 'katex/dist/katex.min.css'; | ||||||
| 
 | 
 | ||||||
|  | 	import { onMount, tick } from 'svelte'; | ||||||
|  | 
 | ||||||
| 	import Name from './Name.svelte'; | 	import Name from './Name.svelte'; | ||||||
| 	import ProfileImage from './ProfileImage.svelte'; | 	import ProfileImage from './ProfileImage.svelte'; | ||||||
| 	import Skeleton from './Skeleton.svelte'; | 	import Skeleton from './Skeleton.svelte'; | ||||||
| 	import { onMount, tick } from 'svelte'; | 	import CodeBlock from './CodeBlock.svelte'; | ||||||
| 
 | 
 | ||||||
| 	export let modelfiles = []; | 	export let modelfiles = []; | ||||||
| 	export let message; | 	export let message; | ||||||
|  | @ -33,6 +32,20 @@ | ||||||
| 	let tooltipInstance = null; | 	let tooltipInstance = null; | ||||||
| 	let speaking = 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('&', '&')}</code>`; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	const { extensions, ...defaults } = marked.getDefaults() as marked.MarkedOptions & { | ||||||
|  | 		// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||||
|  | 		extensions: any; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	$: if (message) { | 	$: if (message) { | ||||||
| 		renderStyling(); | 		renderStyling(); | ||||||
| 	} | 	} | ||||||
|  | @ -45,8 +58,6 @@ | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		renderLatex(); | 		renderLatex(); | ||||||
| 		hljs.highlightAll(); |  | ||||||
| 		createCopyCodeBlockButton(); |  | ||||||
| 
 | 
 | ||||||
| 		if (message.info) { | 		if (message.info) { | ||||||
| 			tooltipInstance = tippy(`#info-${message.id}`, { | 			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 = () => { | 	const renderLatex = () => { | ||||||
| 		let chatMessageElements = document.getElementsByClassName('chat-assistant'); | 		let chatMessageElements = document.getElementsByClassName('chat-assistant'); | ||||||
| 		// let lastChatMessageElement = chatMessageElements[chatMessageElements.length - 1]; | 		// let lastChatMessageElement = chatMessageElements[chatMessageElements.length - 1]; | ||||||
|  | @ -292,7 +238,20 @@ | ||||||
| 									</div> | 									</div> | ||||||
| 								</div> | 								</div> | ||||||
| 							{:else} | 							{: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} | ||||||
| 
 | 
 | ||||||
| 							{#if message.done} | 							{#if message.done} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek