File size: 2,779 Bytes
502cb81
4b8b411
 
60216ec
4b8b411
60216ec
 
 
502cb81
0ffe6c3
0bcf467
 
ce92b65
502cb81
25a5986
 
 
 
eb79b3d
 
 
 
502cb81
 
 
de2ec19
 
 
cd2e1ea
de2ec19
 
 
 
 
cd2e1ea
de2ec19
 
 
502cb81
 
25a5986
502cb81
 
 
 
5213b80
f2e5687
de2ec19
25a5986
 
 
502cb81
 
25a5986
 
 
 
 
 
502cb81
5213b80
502cb81
5213b80
d5e14b5
5213b80
25a5986
 
 
 
 
 
 
502cb81
5213b80
f2e5687
 
5213b80
 
25a5986
de2ec19
60216ec
b34b73b
5213b80
 
 
502cb81
eeca96c
60216ec
5213b80
502cb81
5213b80
a979074
5213b80
502cb81
5213b80
ce92b65
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
<script lang="ts">
	import type { Conversation } from "$lib/types";

	import { createEventDispatcher } from "svelte";

	import CodeSnippets from "./InferencePlaygroundCodeSnippets.svelte";
	import Message from "./InferencePlaygroundMessage.svelte";
	import IconPlus from "../Icons/IconPlus.svelte";

	export let conversation: Conversation;
	export let loading: boolean;
	export let viewCode: boolean;
	export let hfToken: string;

	let shouldScrollToBottom = true;
	let isProgrammaticScroll = true;
	let conversationLength = conversation.messages.length;

	const dispatch = createEventDispatcher<{
		addMessage: void;
		deleteMessage: number;
	}>();

	let messageContainer: HTMLDivElement | null = null;

	function resizeMessageTextAreas() {
		// ideally we would use CSS "field-sizing:content". However, it is currently only supported on Chrome.
		if (messageContainer) {
			const containerScrollTop = messageContainer.scrollTop;
			const textareaEls = messageContainer.querySelectorAll("textarea");
			for (const textarea of textareaEls) {
				textarea.style.height = "0px";
				textarea.style.height = textarea.scrollHeight + "px";
			}
			messageContainer.scrollTop = containerScrollTop;
		}
	}

	function scrollToBottom() {
		if (messageContainer) {
			isProgrammaticScroll = true;
			messageContainer.scrollTop = messageContainer.scrollHeight;
		}
	}

	$: {
		if (conversation.messages.at(-1)) {
			resizeMessageTextAreas();
			if (shouldScrollToBottom) {
				scrollToBottom();
			}
		}
	}

	$: if (conversation.messages.length !== conversationLength) {
		// enable automatic scrolling when new message was added
		conversationLength = conversation.messages.length;
		shouldScrollToBottom = true;
	}
</script>

<div
	class="flex max-h-[calc(100dvh-5.8rem)] flex-col overflow-y-auto overflow-x-hidden @container"
	class:animate-pulse={loading && !conversation.streaming}
	bind:this={messageContainer}
	on:scroll={() => {
		// disable automatic scrolling is user initiates scroll
		if (!isProgrammaticScroll) {
			shouldScrollToBottom = false;
		}
		isProgrammaticScroll = false;
	}}
>
	{#if !viewCode}
		{#each conversation.messages as message, messageIdx}
			<Message
				class="border-b"
				{message}
				{loading}
				on:input={resizeMessageTextAreas}
				on:delete={() => dispatch("deleteMessage", messageIdx)}
				autofocus={!loading && messageIdx === conversation.messages.length - 1}
			/>
		{/each}

		<button
			class="flex px-3.5 py-6 hover:bg-gray-50 md:px-6 dark:hover:bg-gray-800/50"
			on:click={() => dispatch("addMessage")}
			disabled={loading}
		>
			<div class="flex items-center gap-2 !p-0 text-sm font-semibold">
				<IconPlus classNames="text-lg" /> Add message
			</div>
		</button>
	{:else}
		<CodeSnippets {conversation} {hfToken} />
	{/if}
</div>