mirror of
https://github.com/open-webui/open-webui
synced 2025-06-26 18:26:48 +00:00
feat: editable code block
This commit is contained in:
@@ -5,14 +5,16 @@
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { getContext, getAllContexts, onMount } from 'svelte';
|
||||
import { getContext, getAllContexts, onMount, tick, createEventDispatcher } from 'svelte';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
|
||||
import 'highlight.js/styles/github-dark.min.css';
|
||||
|
||||
import PyodideWorker from '$lib/workers/pyodide.worker?worker';
|
||||
import CodeEditor from '$lib/components/common/CodeEditor.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let id = '';
|
||||
|
||||
@@ -20,6 +22,15 @@
|
||||
export let lang = '';
|
||||
export let code = '';
|
||||
|
||||
let _code = '';
|
||||
$: if (code) {
|
||||
updateCode();
|
||||
}
|
||||
|
||||
const updateCode = () => {
|
||||
_code = code;
|
||||
};
|
||||
|
||||
let _token = null;
|
||||
|
||||
let mermaidHtml = null;
|
||||
@@ -32,6 +43,18 @@
|
||||
let result = null;
|
||||
|
||||
let copied = false;
|
||||
let saved = false;
|
||||
|
||||
const saveCode = () => {
|
||||
saved = true;
|
||||
|
||||
code = _code;
|
||||
dispatch('save', code);
|
||||
|
||||
setTimeout(() => {
|
||||
saved = false;
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const copyCode = async () => {
|
||||
copied = true;
|
||||
@@ -233,22 +256,11 @@ __builtins__.input = input`);
|
||||
(async () => {
|
||||
await drawMermaidDiagram();
|
||||
})();
|
||||
} else {
|
||||
// Function to perform the code highlighting
|
||||
const highlightCode = () => {
|
||||
highlightedCode = hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value || code;
|
||||
};
|
||||
|
||||
// Clear the previous timeout if it exists
|
||||
clearTimeout(debounceTimeout);
|
||||
// Set a new timeout to debounce the code highlighting
|
||||
debounceTimeout = setTimeout(highlightCode, 10);
|
||||
}
|
||||
};
|
||||
|
||||
$: if (token) {
|
||||
if (JSON.stringify(token) !== JSON.stringify(_token)) {
|
||||
console.log('hi');
|
||||
_token = token;
|
||||
}
|
||||
}
|
||||
@@ -295,28 +307,50 @@ __builtins__.input = input`);
|
||||
{:else}
|
||||
<button
|
||||
class="copy-code-button bg-none border-none p-1"
|
||||
on:click={() => {
|
||||
on:click={async () => {
|
||||
code = _code;
|
||||
await tick();
|
||||
executePython(code);
|
||||
}}>{$i18n.t('Run')}</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<button class="copy-code-button bg-none border-none p-1" on:click={saveCode}>
|
||||
{saved ? $i18n.t('Saved') : $i18n.t('Save')}
|
||||
</button>
|
||||
|
||||
<button class="copy-code-button bg-none border-none p-1" on:click={copyCode}
|
||||
>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre
|
||||
<div
|
||||
class="language-{lang} rounded-t-none {executing || stdout || stderr || result
|
||||
? ''
|
||||
: 'rounded-b-lg'} overflow-hidden"
|
||||
>
|
||||
<CodeEditor
|
||||
value={code}
|
||||
{id}
|
||||
{lang}
|
||||
on:save={() => {
|
||||
saveCode();
|
||||
}}
|
||||
on:change={(e) => {
|
||||
_code = e.detail.value;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- <pre
|
||||
class=" hljs p-4 px-5 overflow-x-auto"
|
||||
style="border-top-left-radius: 0px; border-top-right-radius: 0px; {(executing ||
|
||||
stdout ||
|
||||
stderr ||
|
||||
result) &&
|
||||
'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code
|
||||
class="language-{lang} rounded-t-none whitespace-pre"
|
||||
>{#if highlightedCode}{@html highlightedCode}{:else}{code}{/if}</code
|
||||
></pre>
|
||||
'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code></code></pre> -->
|
||||
|
||||
<div
|
||||
id="plt-canvas-{id}"
|
||||
|
||||
@@ -56,7 +56,14 @@
|
||||
</script>
|
||||
|
||||
<div bind:this={contentContainerElement}>
|
||||
<Markdown {id} {content} {model} />
|
||||
<Markdown
|
||||
{id}
|
||||
{content}
|
||||
{model}
|
||||
on:update={(e) => {
|
||||
dispatch('update', e.detail);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if floatingButtons}
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
import markedKatexExtension from '$lib/utils/marked/katex-extension';
|
||||
|
||||
import MarkdownTokens from './Markdown/MarkdownTokens.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let id;
|
||||
export let content;
|
||||
@@ -31,5 +34,11 @@
|
||||
</script>
|
||||
|
||||
{#key id}
|
||||
<MarkdownTokens {tokens} {id} />
|
||||
<MarkdownTokens
|
||||
{tokens}
|
||||
{id}
|
||||
on:update={(e) => {
|
||||
dispatch('update', e.detail);
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
<script lang="ts">
|
||||
import DOMPurify from 'dompurify';
|
||||
import { onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { marked, type Token } from 'marked';
|
||||
import { revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
|
||||
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
|
||||
import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
|
||||
import MarkdownInlineTokens from '$lib/components/chat/Messages/Markdown/MarkdownInlineTokens.svelte';
|
||||
import KatexRenderer from './KatexRenderer.svelte';
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import { stringify } from 'postcss';
|
||||
import Collapsible from '$lib/components/common/Collapsible.svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let id: string;
|
||||
export let tokens: Token[];
|
||||
export let top = true;
|
||||
@@ -34,6 +36,12 @@
|
||||
{token}
|
||||
lang={token?.lang ?? ''}
|
||||
code={revertSanitizedResponseContent(token?.text ?? '')}
|
||||
on:save={(e) => {
|
||||
dispatch('update', {
|
||||
oldContent: token.text,
|
||||
newContent: e.detail
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{:else if token.type === 'table'}
|
||||
<div class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full">
|
||||
|
||||
@@ -479,6 +479,15 @@
|
||||
id={message.id}
|
||||
content={message.content}
|
||||
{model}
|
||||
on:update={(e) => {
|
||||
const { oldContent, newContent } = e.detail;
|
||||
|
||||
history.messages[message.id].content = history.messages[
|
||||
message.id
|
||||
].content.replace(oldContent, newContent);
|
||||
|
||||
dispatch('update');
|
||||
}}
|
||||
on:explain={(e) => {
|
||||
dispatch(
|
||||
'submit',
|
||||
|
||||
Reference in New Issue
Block a user