server : (web ui) add copy btn for code blocks

This commit is contained in:
Xuan Son Nguyen 2024-11-09 18:19:24 -04:00
parent 6423c65aa8
commit 22160f000e

View file

@ -12,7 +12,7 @@
.markdown { .markdown {
h1, h2, h3, h4, h5, h6, ul, ol, li { all: revert; } h1, h2, h3, h4, h5, h6, ul, ol, li { all: revert; }
pre { pre {
@apply whitespace-pre-wrap my-4 rounded-lg p-2; @apply whitespace-pre-wrap rounded-lg p-2;
border: 1px solid currentColor; border: 1px solid currentColor;
} }
/* TODO: fix markdown table */ /* TODO: fix markdown table */
@ -234,9 +234,13 @@
import { createApp, defineComponent, shallowRef, computed, h } from './deps_vue.esm-browser.js'; import { createApp, defineComponent, shallowRef, computed, h } from './deps_vue.esm-browser.js';
import { llama } from './completion.js'; import { llama } from './completion.js';
// utility functions
const isString = (x) => !!x.toLowerCase; const isString = (x) => !!x.toLowerCase;
const isNumeric = (n) => !isString(n) && !isNaN(n); const isNumeric = (n) => !isString(n) && !isNaN(n);
const escapeAttr = (str) => str.replace(/>/g, '>').replace(/"/g, '"');
const copyStr = (str) => navigator.clipboard.writeText(str);
// constants
const BASE_URL = localStorage.getItem('base') // for debugging const BASE_URL = localStorage.getItem('base') // for debugging
|| (new URL('.', document.baseURI).href).toString(); // for production || (new URL('.', document.baseURI).href).toString(); // for production
const CONFIG_DEFAULT = { const CONFIG_DEFAULT = {
@ -259,14 +263,23 @@
// markdown support // markdown support
const VueMarkdown = defineComponent( const VueMarkdown = defineComponent(
(props) => { (props) => {
const md = shallowRef(new markdownit(props.options ?? { breaks: true })); const md = shallowRef(new markdownit({ breaks: true }));
for (const plugin of props.plugins ?? []) { const origFenchRenderer = md.value.renderer.rules.fence;
md.value.use(plugin); md.value.renderer.rules.fence = (tokens, idx, ...args) => {
} const content = tokens[idx].content;
const origRendered = origFenchRenderer(tokens, idx, ...args);
return `<div class="relative my-4">
<div class="text-right sticky top-4 mb-2 mr-2 h-0">
<button class="badge btn-mini" onclick="copyStr(${escapeAttr(JSON.stringify(content))})">📋 Copy</button>
</div>
${origRendered}
</div>`;
};
window.copyStr = copyStr;
const content = computed(() => md.value.render(props.source)); const content = computed(() => md.value.render(props.source));
return () => h("div", { innerHTML: content.value }); return () => h("div", { innerHTML: content.value });
}, },
{ props: ["source", "options", "plugins"] } { props: ["source"] }
); );
// coversations is stored in localStorage // coversations is stored in localStorage
@ -512,7 +525,7 @@
this.generateMessage(currConvId); this.generateMessage(currConvId);
}, },
copyMsg(msg) { copyMsg(msg) {
navigator.clipboard.writeText(msg.content); copyStr(msg.content);
}, },
editUserMsgAndRegenerate(msg) { editUserMsgAndRegenerate(msg) {
if (this.isGenerating) return; if (this.isGenerating) return;