File size: 2,501 Bytes
a88830a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a32010b
 
 
 
 
 
4228faa
 
 
a88830a
 
 
 
 
 
 
4228faa
a88830a
6ca8d92
a88830a
 
 
 
 
 
 
 
 
 
a32010b
 
a88830a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
<script lang="ts">
	import { autofocus } from "$lib/attachments/autofocus.js";
	import { TextareaAutosize } from "$lib/spells/textarea-autosize.svelte.js";
	import { conversations } from "$lib/state/conversations.svelte";
	import { cmdOrCtrl } from "$lib/utils/platform.js";

	const multiple = $derived(conversations.active.length > 1);
	const loading = $derived(conversations.generating);

	let input = $state("");

	async function onKeydown(event: KeyboardEvent) {
		if (loading) return;
		const ctrlOrMeta = event.ctrlKey || event.metaKey;

		if (ctrlOrMeta && event.key === "Enter") {
			sendMessage();
		}
	}

	async function sendMessage() {
		const c = conversations.active;
		await Promise.all(c.map(c => c.addMessage({ role: "user", content: input })));
		c.forEach(c => c.genNextMessage());
		input = "";
	}

	const autosized = new TextareaAutosize();
</script>

<svelte:window onkeydown={onKeydown} />

<div class="mt-auto px-2">
	<label
		class="flex w-full items-end rounded-[32px] bg-gray-200 p-2 pl-6 outline-gray-400 focus-within:outline-2 dark:bg-gray-800"
	>
		<textarea
			placeholder="Enter your message"
			class="max-h-100 flex-1 resize-none self-center outline-none"
			bind:value={input}
			{@attach autosized.attachment}
			{@attach autofocus()}
		></textarea>
		<button
			onclick={() => {
				if (loading) conversations.stopGenerating();
				else sendMessage();
			}}
			type="button"
			class={[
				"flex items-center justify-center gap-2 rounded-full px-3.5 py-2.5 text-sm font-medium text-white focus:ring-4 focus:ring-gray-300 focus:outline-hidden dark:focus:ring-gray-700",
				loading && "bg-red-900 hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700",
				!loading && "bg-black hover:bg-gray-900 dark:bg-blue-600 dark:hover:bg-blue-700",
			]}
		>
			{#if loading}
				<div class="flex flex-none items-center gap-[3px]">
					<span class="mr-2">
						{#if conversations.active.some(c => c.data.streaming)}
							Stop
						{:else}
							Cancel
						{/if}
					</span>
					{#each { length: 3 } as _, i}
						<div
							class="h-1 w-1 flex-none animate-bounce rounded-full bg-gray-200 dark:bg-gray-100"
							style="animation-delay: {(i + 1) * 0.25}s;"
						></div>
					{/each}
				</div>
			{:else}
				{multiple ? "Run all" : "Run"}
				<span class="inline-flex gap-0.5 rounded-sm border border-white/20 bg-white/10 px-0.5 text-xs text-white/70">
					{cmdOrCtrl}<span class="translate-y-px">↵</span>
				</span>
			{/if}
		</button>
	</label>
</div>