File size: 3,131 Bytes
502cb81
ca3d512
4b8b411
5c869f5
4b8b411
60216ec
 
 
502cb81
0ffe6c3
0bcf467
 
ce92b65
1cd3833
502cb81
25a5986
 
 
 
eb79b3d
 
 
 
502cb81
 
 
5c869f5
de2ec19
5c869f5
de2ec19
cd2e1ea
de2ec19
 
 
 
 
cd2e1ea
de2ec19
 
 
502cb81
 
25a5986
502cb81
 
 
 
5213b80
f2e5687
de2ec19
25a5986
 
 
502cb81
 
25a5986
 
 
 
 
 
5c869f5
 
502cb81
5213b80
60c7941
 
502cb81
1cd3833
92f7aa1
 
d5e14b5
5213b80
25a5986
 
 
 
 
 
 
502cb81
5213b80
f2e5687
 
5213b80
 
25a5986
de2ec19
60216ec
b34b73b
5213b80
 
 
502cb81
eeca96c
60216ec
5213b80
502cb81
5213b80
a979074
5213b80
502cb81
5213b80
8709c70
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
<script lang="ts">
	import type { Conversation } from "$lib/components/InferencePlayground/types";

	import { createEventDispatcher, tick } 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;
	export let compareActive: boolean;

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

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

	let messageContainer: HTMLDivElement | null = null;

	async function resizeMessageTextAreas() {
		// ideally we would use CSS "field-sizing:content". However, it is currently only supported on Chrome.
		await tick();
		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;
	}

	$: viewCode, resizeMessageTextAreas();
</script>

<svelte:window on:resize={resizeMessageTextAreas} />

<div
	class="flex flex-col overflow-y-auto overflow-x-hidden @container {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}
	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} on:closeCode />
	{/if}
</div>