File size: 3,990 Bytes
502cb81 5851d63 4b8b411 5851d63 4b8b411 b2170a7 60216ec 502cb81 5851d63 502cb81 5851d63 25a5986 5851d63 502cb81 5851d63 de2ec19 502cb81 25a5986 502cb81 5851d63 f2e5687 25a5986 502cb81 5851d63 5c869f5 5851d63 b2170a7 502cb81 5213b80 502cb81 73b6f4f 92f7aa1 d5e14b5 5213b80 5851d63 25a5986 502cb81 5213b80 5851d63 5213b80 502cb81 eeca96c 5851d63 5213b80 502cb81 8c5a2cf 64cfbce b2170a7 5213b80 502cb81 5213b80 25c63d0 5213b80 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
<script lang="ts">
import { run } from "svelte/legacy";
import type { Conversation } from "$lib/types.js";
import IconPlus from "~icons/carbon/add";
import CodeSnippets from "./InferencePlaygroundCodeSnippets.svelte";
interface Props {
conversation: Conversation;
loading: boolean;
viewCode: boolean;
compareActive: boolean;
}
let { conversation = $bindable(), loading, viewCode, compareActive }: Props = $props();
let shouldScrollToBottom = $state(true);
let isProgrammaticScroll = $state(true);
let conversationLength = $state(conversation.messages.length);
let messageContainer: HTMLDivElement | null = $state(null);
function scrollToBottom() {
if (messageContainer) {
isProgrammaticScroll = true;
messageContainer.scrollTop = messageContainer.scrollHeight;
}
}
run(() => {
if (conversation.messages.at(-1)) {
if (shouldScrollToBottom) {
scrollToBottom();
}
}
});
run(() => {
if (conversation.messages.length !== conversationLength) {
// enable automatic scrolling when new message was added
conversationLength = conversation.messages.length;
shouldScrollToBottom = true;
}
});
function addMessage() {
const msgs = conversation.messages.slice();
conversation.messages = [
...msgs,
{
role: msgs.at(-1)?.role === "user" ? "assistant" : "user",
content: "",
},
];
conversation = conversation;
}
function deleteMessage(idx: number) {
conversation.messages.splice(idx, 1);
conversation = conversation;
}
</script>
<div
class="@container flex flex-col overflow-x-hidden overflow-y-auto {compareActive
? 'max-h-[calc(100dvh-5.8rem-2.5rem-75px)] md:max-h-[calc(100dvh-5.8rem-2.5rem)]'
: 'max-h-[calc(100dvh-5.8rem-2.5rem-75px)] md:max-h-[calc(100dvh-5.8rem)]'}"
class:animate-pulse={loading && !conversation.streaming}
bind:this={messageContainer}
onscroll={() => {
// disable automatic scrolling is user initiates scroll
if (!isProgrammaticScroll) {
shouldScrollToBottom = false;
}
isProgrammaticScroll = false;
}}
>
{#if !viewCode}
{#each conversation.messages as msg, idx}
<div
class=" group/message group grid grid-cols-[1fr_2.5rem] items-start gap-2 border-b px-3.5 pt-4 pb-6 hover:bg-gray-100/70 @-2xl:grid-cols-[130px_1fr_2.5rem] @2xl:grid-rows-1 @2xl:gap-4 @2xl:px-6 dark:border-gray-800 dark:hover:bg-gray-800/30"
class:pointer-events-none={loading}
>
<div class="col-span-2 pt-3 pb-1 text-sm font-semibold uppercase @2xl:col-span-1 @2xl:pb-2">
{msg.role}
</div>
<!-- svelte-ignore a11y_autofocus -->
<!-- svelte-ignore a11y_positive_tabindex -->
<textarea
autofocus={idx === conversation.messages.length - 1}
bind:value={conversation.messages[idx]!.content}
placeholder="Enter {msg.role} message"
class="resize-none overflow-hidden rounded-sm bg-transparent px-2 py-2.5 ring-gray-100 outline-none group-hover/message:ring-3 hover:resize-y hover:bg-white focus:resize-y focus:bg-white focus:ring-3 @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
rows="1"
tabindex="2"
></textarea>
<button
tabindex="0"
onclick={() => deleteMessage(idx)}
type="button"
class="mt-1.5 size-8 rounded-lg border border-gray-200 bg-white text-xs font-medium text-gray-900 group-hover/message:block hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 focus:outline-hidden sm:hidden dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
>✕</button
>
</div>
{/each}
<button
class="flex px-3.5 py-6 hover:bg-gray-50 md:px-6 dark:hover:bg-gray-800/50"
onclick={addMessage}
disabled={loading}
>
<div class="flex items-center gap-2 p-0! text-sm font-semibold">
<div class="text-lg">
<IconPlus />
</div>
Add message
</div>
</button>
{:else}
<CodeSnippets {conversation} on:closeCode />
{/if}
</div>
|