File size: 2,194 Bytes
502cb81
0ce274f
1778c9e
0ce274f
 
b1426d3
f48efbb
502cb81
5851d63
1778c9e
5851d63
1778c9e
5851d63
502cb81
1778c9e
a88830a
5851d63
2dd779d
 
 
5851d63
0ce274f
5c869f5
2dd779d
f9963a5
2dd779d
0ce274f
2dd779d
0ce274f
 
 
 
 
 
 
7e80e42
2dd779d
b2170a7
1778c9e
 
f9963a5
f36471e
 
1778c9e
f36471e
1778c9e
f36471e
 
1778c9e
 
f36471e
502cb81
5213b80
502cb81
a88830a
1778c9e
5213b80
502cb81
5213b80
912969f
4228faa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5213b80
1778c9e
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
<script lang="ts">
	import { ScrollState } from "$lib/spells/scroll-state.svelte";
	import { type ConversationClass } from "$lib/state/conversations.svelte";
	import { watch } from "runed";
	import { tick } from "svelte";
	import CodeSnippets from "./code-snippets.svelte";
	import Message from "./message.svelte";

	interface Props {
		conversation: ConversationClass;
		viewCode: boolean;
		onCloseCode: () => void;
	}

	const { conversation, viewCode, onCloseCode }: Props = $props();

	let messageContainer: HTMLDivElement | null = $state(null);
	const scrollState = new ScrollState({
		element: () => messageContainer,
		offset: { bottom: 100 },
	});
	const atBottom = $derived(scrollState.arrived.bottom);

	watch(
		() => conversation.data.messages?.at(-1)?.content,
		() => {
			const shouldScroll = atBottom && !scrollState.isScrolling;
			if (!shouldScroll) return;
			try {
				tick().then(() => {
					scrollState.scrollToBottom();
				});
			} catch {
				// noop
			}
		},
	);

	async function regenMessage(idx: number) {
		// TODO: migrate to new logic
		const msg = conversation.data.messages?.[idx];
		if (!msg) return;
		if (msg.role === "user") {
			await conversation.deleteMessages(idx + 1);
		} else {
			await conversation.deleteMessages(idx);
		}

		conversation.stopGenerating();
		conversation.genNextMessage();
	}
</script>

<div
	class="@container flex h-full flex-col overflow-x-hidden overflow-y-auto"
	class:animate-pulse={conversation.generating && !conversation.data.streaming}
	bind:this={messageContainer}
>
	{#if !viewCode}
		{#if conversation.data.messages}
			{#each conversation.data.messages as message, index}
				<Message
					{message}
					{index}
					{conversation}
					onDelete={() => conversation.deleteMessage(index)}
					onRegen={() => regenMessage(index)}
				/>
			{:else}
				<div class="m-auto flex flex-col items-center gap-2 text-center px-4 text-balance">
					<h1 class="text-2xl font-semibold">Welcome to Hugging Face Inference Playground</h1>
					<p class="text-lg text-gray-500">Try hundreds of models on different providers</p>
				</div>
			{/each}
		{/if}
	{:else}
		<CodeSnippets {conversation} {onCloseCode} />
	{/if}
</div>