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…
Reference in a new issue