Browse Source

feat: image upload support

Timothy J. Baek 1 year ago
parent
commit
9b12cdcf83

+ 32 - 31
src/lib/components/chat/MessageInput.svelte

@@ -14,7 +14,7 @@
 
 
 	export let files = [];
 	export let files = [];
 
 
-	export let fileUploadEnabled = false;
+	export let fileUploadEnabled = true;
 	export let speechRecognitionEnabled = true;
 	export let speechRecognitionEnabled = true;
 	export let speechRecognitionListening = false;
 	export let speechRecognitionListening = false;
 
 
@@ -84,40 +84,40 @@
 	};
 	};
 </script>
 </script>
 
 
-<div class="fixed bottom-0 w-full bg-white dark:bg-gray-800">
-	<div class=" absolute right-0 left-0 bottom-0 mb-20">
-		<div class="max-w-3xl px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0">
-			{#if messages.length == 0 && suggestionPrompts.length !== 0}
+<div class="fixed bottom-0 w-full">
+	<div class="px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
+		{#if messages.length == 0 && suggestionPrompts.length !== 0}
+			<div class="max-w-3xl">
 				<Suggestions {suggestionPrompts} {submitPrompt} />
 				<Suggestions {suggestionPrompts} {submitPrompt} />
-			{/if}
+			</div>
+		{/if}
 
 
-			{#if autoScroll === false && messages.length > 0}
-				<div class=" flex justify-center mb-4">
-					<button
-						class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
-						on:click={() => {
-							autoScroll = true;
-							window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
-						}}
+		{#if autoScroll === false && messages.length > 0}
+			<div class=" flex justify-center mb-4">
+				<button
+					class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
+					on:click={() => {
+						autoScroll = true;
+						window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
+					}}
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 20 20"
+						fill="currentColor"
+						class="w-5 h-5"
 					>
 					>
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 20 20"
-							fill="currentColor"
-							class="w-5 h-5"
-						>
-							<path
-								fill-rule="evenodd"
-								d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z"
-								clip-rule="evenodd"
-							/>
-						</svg>
-					</button>
-				</div>
-			{/if}
-		</div>
+						<path
+							fill-rule="evenodd"
+							d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</button>
+			</div>
+		{/if}
 	</div>
 	</div>
-	<div>
+	<div class="bg-white dark:bg-gray-800">
 		<div class="max-w-3xl px-2.5 -mb-0.5 mx-auto inset-x-0">
 		<div class="max-w-3xl px-2.5 -mb-0.5 mx-auto inset-x-0">
 			<div class="bg-gradient-to-t from-white dark:from-gray-800 from-40% pb-2">
 			<div class="bg-gradient-to-t from-white dark:from-gray-800 from-40% pb-2">
 				<input
 				<input
@@ -136,6 +136,7 @@
 								}
 								}
 							];
 							];
 							inputFiles = null;
 							inputFiles = null;
+							filesInputElement.value = '';
 						};
 						};
 
 
 						if (
 						if (

+ 26 - 12
src/lib/components/chat/Messages.svelte

@@ -15,6 +15,7 @@
 	export let sendPrompt: Function;
 	export let sendPrompt: Function;
 	export let regenerateResponse: Function;
 	export let regenerateResponse: Function;
 
 
+	export let bottomPadding = false;
 	export let autoScroll;
 	export let autoScroll;
 	export let selectedModels;
 	export let selectedModels;
 	export let history = {};
 	export let history = {};
@@ -31,6 +32,13 @@
 		})();
 		})();
 	}
 	}
 
 
+	$: if (autoScroll && bottomPadding) {
+		(async () => {
+			await tick();
+			window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
+		})();
+	}
+
 	const speakMessage = (message) => {
 	const speakMessage = (message) => {
 		const speak = new SpeechSynthesisUtterance(message);
 		const speak = new SpeechSynthesisUtterance(message);
 		speechSynthesis.speak(speak);
 		speechSynthesis.speak(speak);
@@ -184,7 +192,8 @@
 			parentId: history.messages[messageId].parentId,
 			parentId: history.messages[messageId].parentId,
 			childrenIds: [],
 			childrenIds: [],
 			role: 'user',
 			role: 'user',
-			content: userPrompt
+			content: userPrompt,
+			...(history.messages[messageId].files && { files: history.messages[messageId].files })
 		};
 		};
 
 
 		let messageParentId = history.messages[messageId].parentId;
 		let messageParentId = history.messages[messageId].parentId;
@@ -425,6 +434,18 @@
 								class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:my-0 prose-p:-mb-4 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-6 prose-ol:-mb-6 prose-li:-mb-4 whitespace-pre-line"
 								class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:my-0 prose-p:-mb-4 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-6 prose-ol:-mb-6 prose-li:-mb-4 whitespace-pre-line"
 							>
 							>
 								{#if message.role == 'user'}
 								{#if message.role == 'user'}
+									{#if message.files}
+										<div class="my-3 w-full flex overflow-x-auto space-x-2">
+											{#each message.files as file}
+												<div>
+													{#if file.type === 'image'}
+														<img src={file.url} alt="input" class=" max-h-96 rounded-lg" />
+													{/if}
+												</div>
+											{/each}
+										</div>
+									{/if}
+
 									{#if message?.edit === true}
 									{#if message?.edit === true}
 										<div class=" w-full">
 										<div class=" w-full">
 											<textarea
 											<textarea
@@ -458,17 +479,6 @@
 										</div>
 										</div>
 									{:else}
 									{:else}
 										<div class="w-full">
 										<div class="w-full">
-											{#if message.files}
-												<div class="my-3">
-													{#each message.files as file}
-														<div>
-															{#if file.type === 'image'}
-																<img src={file.url} alt="input" class=" max-h-96" />
-															{/if}
-														</div>
-													{/each}
-												</div>
-											{/if}
 											<pre id="user-message">{message.content}</pre>
 											<pre id="user-message">{message.content}</pre>
 
 
 											<div class=" flex justify-start space-x-1">
 											<div class=" flex justify-start space-x-1">
@@ -889,4 +899,8 @@
 			</div>
 			</div>
 		</div>
 		</div>
 	{/each}
 	{/each}
+
+	{#if bottomPadding}
+		<div class=" mb-10" />
+	{/if}
 {/if}
 {/if}

+ 15 - 2
src/routes/(app)/+page.svelte

@@ -50,6 +50,10 @@
 		messages = [];
 		messages = [];
 	}
 	}
 
 
+	$: if (files) {
+		console.log(files);
+	}
+
 	onMount(async () => {
 	onMount(async () => {
 		await chatId.set(uuidv4());
 		await chatId.set(uuidv4());
 
 
@@ -146,7 +150,15 @@
 					...messages
 					...messages
 				]
 				]
 					.filter((message) => message)
 					.filter((message) => message)
-					.map((message) => ({ role: message.role, content: message.content })),
+					.map((message) => ({
+						role: message.role,
+						content: message.content,
+						...(message.files && {
+							images: message.files
+								.filter((file) => file.type === 'image')
+								.map((file) => file.url.slice(file.url.indexOf(',') + 1))
+						})
+					})),
 				options: {
 				options: {
 					seed: $settings.seed ?? undefined,
 					seed: $settings.seed ?? undefined,
 					temperature: $settings.temperature ?? undefined,
 					temperature: $settings.temperature ?? undefined,
@@ -548,6 +560,7 @@
 				bind:history
 				bind:history
 				bind:messages
 				bind:messages
 				bind:autoScroll
 				bind:autoScroll
+				bottomPadding={files.length > 0}
 				{sendPrompt}
 				{sendPrompt}
 				{regenerateResponse}
 				{regenerateResponse}
 			/>
 			/>
@@ -555,8 +568,8 @@
 	</div>
 	</div>
 
 
 	<MessageInput
 	<MessageInput
-		bind:prompt
 		bind:files
 		bind:files
+		bind:prompt
 		bind:autoScroll
 		bind:autoScroll
 		suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [
 		suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [
 			{
 			{

+ 11 - 12
src/routes/(app)/c/[id]/+page.svelte

@@ -51,17 +51,6 @@
 		messages = [];
 		messages = [];
 	}
 	}
 
 
-	// onMount(async () => {
-	// 	let chat = await loadChat();
-
-	// 	await tick();
-	// 	if (chat) {
-	// 		loaded = true;
-	// 	} else {
-	// 		await goto('/');
-	// 	}
-	// });
-
 	$: if ($page.params.id) {
 	$: if ($page.params.id) {
 		(async () => {
 		(async () => {
 			let chat = await loadChat();
 			let chat = await loadChat();
@@ -173,7 +162,15 @@
 					...messages
 					...messages
 				]
 				]
 					.filter((message) => message)
 					.filter((message) => message)
-					.map((message) => ({ role: message.role, content: message.content })),
+					.map((message) => ({
+						role: message.role,
+						content: message.content,
+						...(message.files && {
+							images: message.files
+								.filter((file) => file.type === 'image')
+								.map((file) => file.url.slice(file.url.indexOf(',') + 1))
+						})
+					})),
 				options: {
 				options: {
 					seed: $settings.seed ?? undefined,
 					seed: $settings.seed ?? undefined,
 					temperature: $settings.temperature ?? undefined,
 					temperature: $settings.temperature ?? undefined,
@@ -579,6 +576,7 @@
 					bind:history
 					bind:history
 					bind:messages
 					bind:messages
 					bind:autoScroll
 					bind:autoScroll
+					bottomPadding={files.length > 0}
 					{sendPrompt}
 					{sendPrompt}
 					{regenerateResponse}
 					{regenerateResponse}
 				/>
 				/>
@@ -586,6 +584,7 @@
 		</div>
 		</div>
 
 
 		<MessageInput
 		<MessageInput
+			bind:files
 			bind:prompt
 			bind:prompt
 			bind:autoScroll
 			bind:autoScroll
 			suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [
 			suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [