refac: response message

This commit is contained in:
Timothy J. Baek 2024-01-22 03:33:49 -08:00
parent 1abe5a5487
commit e758855590
2 changed files with 67 additions and 72 deletions

View 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>

View file

@ -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('&amp;', '&')}</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}