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

	import { tick } from "svelte";

	import IconPlus from "~icons/carbon/add";
	import CodeSnippets from "./InferencePlaygroundCodeSnippets.svelte";
	import Message from "./InferencePlaygroundMessage.svelte";

	export let conversation: Conversation;
	export let loading: boolean;
	export let viewCode: boolean;
	export let compareActive: boolean;

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

	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();

	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>

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

<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}
	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"
				bind:message
				{loading}
				on:input={resizeMessageTextAreas}
				on:delete={() => 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={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>