feat: preview html

This commit is contained in:
Timothy Jaeryang Baek 2025-05-16 21:47:43 +04:00
parent 07b5e84221
commit 54dc24986f
8 changed files with 73 additions and 28 deletions

View File

@ -4,7 +4,7 @@
const i18n = getContext('i18n'); const i18n = getContext('i18n');
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import { chatId, settings, showArtifacts, showControls } from '$lib/stores'; import { artifactCode, chatId, settings, showArtifacts, showControls } from '$lib/stores';
import XMark from '../icons/XMark.svelte'; import XMark from '../icons/XMark.svelte';
import { copyToClipboard, createMessagesList } from '$lib/utils'; import { copyToClipboard, createMessagesList } from '$lib/utils';
import ArrowsPointingOut from '../icons/ArrowsPointingOut.svelte'; import ArrowsPointingOut from '../icons/ArrowsPointingOut.svelte';
@ -180,7 +180,14 @@
} }
}; };
onMount(() => {}); onMount(() => {
artifactCode.subscribe((value) => {
if (contents) {
const codeIdx = contents.findIndex((content) => content.content.includes(value));
selectedContentIdx = codeIdx !== -1 ? codeIdx : 0;
}
});
});
</script> </script>
<div class=" w-full h-full relative flex flex-col bg-gray-50 dark:bg-gray-850"> <div class=" w-full h-full relative flex flex-col bg-gray-50 dark:bg-gray-850">

View File

@ -957,8 +957,6 @@
? (e.key === 'Enter' || e.keyCode === 13) && isCtrlPressed ? (e.key === 'Enter' || e.keyCode === 13) && isCtrlPressed
: (e.key === 'Enter' || e.keyCode === 13) && !e.shiftKey; : (e.key === 'Enter' || e.keyCode === 13) && !e.shiftKey;
console.log('Enter pressed:', enterPressed);
if (enterPressed) { if (enterPressed) {
e.preventDefault(); e.preventDefault();
} }

View File

@ -17,6 +17,7 @@
import ChevronUp from '$lib/components/icons/ChevronUp.svelte'; import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
import ChevronUpDown from '$lib/components/icons/ChevronUpDown.svelte'; import ChevronUpDown from '$lib/components/icons/ChevronUpDown.svelte';
import CommandLine from '$lib/components/icons/CommandLine.svelte'; import CommandLine from '$lib/components/icons/CommandLine.svelte';
import Cube from '$lib/components/icons/Cube.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@ -24,9 +25,11 @@
export let onSave = (e) => {}; export let onSave = (e) => {};
export let onCode = (e) => {}; export let onCode = (e) => {};
export let onPreview = (e) => {};
export let save = false; export let save = false;
export let run = true; export let run = true;
export let preview = false;
export let collapsed = false; export let collapsed = false;
export let token; export let token;
@ -88,6 +91,10 @@
}, 1000); }, 1000);
}; };
const previewCode = () => {
onPreview(code);
};
const checkPythonCode = (str) => { const checkPythonCode = (str) => {
// Check if the string contains typical Python syntax characters // Check if the string contains typical Python syntax characters
const pythonSyntax = [ const pythonSyntax = [
@ -430,7 +437,7 @@
class="flex gap-1 items-center bg-none border-none bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5" class="flex gap-1 items-center bg-none border-none bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
on:click={collapseCodeBlock} on:click={collapseCodeBlock}
> >
<div> <div class=" -translate-y-[0.5px]">
<ChevronUpDown className="size-3" /> <ChevronUpDown className="size-3" />
</div> </div>
@ -439,6 +446,21 @@
</div> </div>
</button> </button>
{#if preview && ['html', 'svg'].includes(lang)}
<button
class="flex gap-1 items-center run-code-button bg-none border-none bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
on:click={previewCode}
>
<div class=" -translate-y-[0.5px]">
<Cube className="size-3" />
</div>
<div>
{$i18n.t('Preview')}
</div>
</button>
{/if}
{#if ($config?.features?.enable_code_execution ?? true) && (lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code)))} {#if ($config?.features?.enable_code_execution ?? true) && (lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code)))}
{#if executing} {#if executing}
<div class="run-code-button bg-none border-none p-1 cursor-not-allowed"> <div class="run-code-button bg-none border-none p-1 cursor-not-allowed">
@ -453,7 +475,7 @@
executePython(code); executePython(code);
}} }}
> >
<div> <div class=" -translate-y-[0.5px]">
<CommandLine className="size-3" /> <CommandLine className="size-3" />
</div> </div>

View File

@ -1,10 +1,17 @@
<script> <script>
import { onDestroy, onMount, tick, getContext, createEventDispatcher } from 'svelte'; import { onDestroy, onMount, tick, getContext } from 'svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
import Markdown from './Markdown.svelte'; import Markdown from './Markdown.svelte';
import { chatId, mobile, settings, showArtifacts, showControls, showOverview } from '$lib/stores'; import {
artifactCode,
chatId,
mobile,
settings,
showArtifacts,
showControls,
showOverview
} from '$lib/stores';
import FloatingButtons from '../ContentRenderer/FloatingButtons.svelte'; import FloatingButtons from '../ContentRenderer/FloatingButtons.svelte';
import { createMessagesList } from '$lib/utils'; import { createMessagesList } from '$lib/utils';
@ -15,8 +22,10 @@
export let sources = null; export let sources = null;
export let save = false; export let save = false;
export let preview = false;
export let floatingButtons = true; export let floatingButtons = true;
export let onUpdate = () => {};
export let onSourceClick = () => {}; export let onSourceClick = () => {};
export let onTaskClick = () => {}; export let onTaskClick = () => {};
@ -122,6 +131,7 @@
{content} {content}
{model} {model}
{save} {save}
{preview}
sourceIds={(sources ?? []).reduce((acc, s) => { sourceIds={(sources ?? []).reduce((acc, s) => {
let ids = []; let ids = [];
s.document.forEach((document, index) => { s.document.forEach((document, index) => {
@ -154,8 +164,12 @@
}, [])} }, [])}
{onSourceClick} {onSourceClick}
{onTaskClick} {onTaskClick}
onUpdate={(value) => { {onUpdate}
dispatch('update', value); onPreview={async (value) => {
await artifactCode.set(value);
await showControls.set(true);
await showArtifacts.set(true);
await showOverview.set(false);
}} }}
onCode={(value) => { onCode={(value) => {
const { lang, code } = value; const { lang, code } = value;

View File

@ -12,11 +12,13 @@
export let content; export let content;
export let model = null; export let model = null;
export let save = false; export let save = false;
export let preview = false;
export let sourceIds = []; export let sourceIds = [];
export let onUpdate = () => {}; export let onUpdate = () => {};
export let onCode = () => {}; export let onCode = () => {};
export let onPreview = () => {};
export let onSourceClick = () => {}; export let onSourceClick = () => {};
export let onTaskClick = () => {}; export let onTaskClick = () => {};
@ -40,5 +42,15 @@
</script> </script>
{#key id} {#key id}
<MarkdownTokens {tokens} {id} {save} {onTaskClick} {onSourceClick} {onUpdate} {onCode} /> <MarkdownTokens
{tokens}
{id}
{save}
{preview}
{onTaskClick}
{onSourceClick}
{onUpdate}
{onCode}
{onPreview}
/>
{/key} {/key}

View File

@ -29,9 +29,11 @@
export let attributes = {}; export let attributes = {};
export let save = false; export let save = false;
export let preview = false;
export let onUpdate: Function = () => {}; export let onUpdate: Function = () => {};
export let onCode: Function = () => {}; export let onCode: Function = () => {};
export let onPreview: Function = () => {};
export let onTaskClick: Function = () => {}; export let onTaskClick: Function = () => {};
export let onSourceClick: Function = () => {}; export let onSourceClick: Function = () => {};
@ -95,7 +97,9 @@
code={token?.text ?? ''} code={token?.text ?? ''}
{attributes} {attributes}
{save} {save}
{preview}
{onCode} {onCode}
{onPreview}
onSave={(value) => { onSave={(value) => {
onUpdate({ onUpdate({
raw: token.raw, raw: token.raw,

View File

@ -806,6 +806,7 @@
sources={message.sources} sources={message.sources}
floatingButtons={message?.done && !readOnly} floatingButtons={message?.done && !readOnly}
save={!readOnly} save={!readOnly}
preview={!readOnly}
{model} {model}
onTaskClick={async (e) => { onTaskClick={async (e) => {
console.log(e); console.log(e);
@ -840,28 +841,13 @@
onAddMessages={({ modelId, parentId, messages }) => { onAddMessages={({ modelId, parentId, messages }) => {
addMessages({ modelId, parentId, messages }); addMessages({ modelId, parentId, messages });
}} }}
on:update={(e) => { onUpdate={({ raw, oldContent, newContent }) => {
const { raw, oldContent, newContent } = e.detail;
history.messages[message.id].content = history.messages[ history.messages[message.id].content = history.messages[
message.id message.id
].content.replace(raw, raw.replace(oldContent, newContent)); ].content.replace(raw, raw.replace(oldContent, newContent));
updateChat(); updateChat();
}} }}
on:select={(e) => {
const { type, content } = e.detail;
if (type === 'explain') {
submitMessage(
message.id,
`Explain this section to me in more detail\n\n\`\`\`\n${content}\n\`\`\``
);
} else if (type === 'ask') {
const input = e.detail?.input ?? '';
submitMessage(message.id, `\`\`\`\n${content}\n\`\`\`\n${input}`);
}
}}
/> />
{/if} {/if}

View File

@ -74,6 +74,8 @@ export const showOverview = writable(false);
export const showArtifacts = writable(false); export const showArtifacts = writable(false);
export const showCallOverlay = writable(false); export const showCallOverlay = writable(false);
export const artifactCode = writable(null);
export const temporaryChatEnabled = writable(false); export const temporaryChatEnabled = writable(false);
export const scrollPaginationEnabled = writable(false); export const scrollPaginationEnabled = writable(false);
export const currentChatPage = writable(1); export const currentChatPage = writable(1);