forked from open-webui/open-webui
		
	feat: image upload support
This commit is contained in:
		
							parent
							
								
									fb1f8b167c
								
							
						
					
					
						commit
						9b12cdcf83
					
				
					 4 changed files with 84 additions and 57 deletions
				
			
		|  | @ -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,11 +84,12 @@ | ||||||
| 	}; | 	}; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <div class="fixed bottom-0 w-full bg-white dark:bg-gray-800"> | <div class="fixed bottom-0 w-full"> | ||||||
| 	<div class=" absolute right-0 left-0 bottom-0 mb-20"> | 	<div class="px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center"> | ||||||
| 		<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} | 		{#if messages.length == 0 && suggestionPrompts.length !== 0} | ||||||
|  | 			<div class="max-w-3xl"> | ||||||
| 				<Suggestions {suggestionPrompts} {submitPrompt} /> | 				<Suggestions {suggestionPrompts} {submitPrompt} /> | ||||||
|  | 			</div> | ||||||
| 		{/if} | 		{/if} | ||||||
| 
 | 
 | ||||||
| 		{#if autoScroll === false && messages.length > 0} | 		{#if autoScroll === false && messages.length > 0} | ||||||
|  | @ -116,8 +117,7 @@ | ||||||
| 			</div> | 			</div> | ||||||
| 		{/if} | 		{/if} | ||||||
| 	</div> | 	</div> | ||||||
| 	</div> | 	<div class="bg-white dark:bg-gray-800"> | ||||||
| 	<div> |  | ||||||
| 		<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 ( | ||||||
|  |  | ||||||
|  | @ -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} | ||||||
|  |  | ||||||
|  | @ -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 ?? [ | ||||||
| 			{ | 			{ | ||||||
|  |  | ||||||
|  | @ -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 ?? [ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek