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 fileUploadEnabled = false; | ||||
| 	export let fileUploadEnabled = true; | ||||
| 	export let speechRecognitionEnabled = true; | ||||
| 	export let speechRecognitionListening = false; | ||||
| 
 | ||||
|  | @ -84,11 +84,12 @@ | |||
| 	}; | ||||
| </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"> | ||||
| <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} /> | ||||
| 			</div> | ||||
| 		{/if} | ||||
| 
 | ||||
| 		{#if autoScroll === false && messages.length > 0} | ||||
|  | @ -116,8 +117,7 @@ | |||
| 			</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="bg-gradient-to-t from-white dark:from-gray-800 from-40% pb-2"> | ||||
| 				<input | ||||
|  | @ -136,6 +136,7 @@ | |||
| 								} | ||||
| 							]; | ||||
| 							inputFiles = null; | ||||
| 							filesInputElement.value = ''; | ||||
| 						}; | ||||
| 
 | ||||
| 						if ( | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| 	export let sendPrompt: Function; | ||||
| 	export let regenerateResponse: Function; | ||||
| 
 | ||||
| 	export let bottomPadding = false; | ||||
| 	export let autoScroll; | ||||
| 	export let selectedModels; | ||||
| 	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 speak = new SpeechSynthesisUtterance(message); | ||||
| 		speechSynthesis.speak(speak); | ||||
|  | @ -184,7 +192,8 @@ | |||
| 			parentId: history.messages[messageId].parentId, | ||||
| 			childrenIds: [], | ||||
| 			role: 'user', | ||||
| 			content: userPrompt | ||||
| 			content: userPrompt, | ||||
| 			...(history.messages[messageId].files && { files: history.messages[messageId].files }) | ||||
| 		}; | ||||
| 
 | ||||
| 		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" | ||||
| 							> | ||||
| 								{#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} | ||||
| 										<div class=" w-full"> | ||||
| 											<textarea | ||||
|  | @ -458,17 +479,6 @@ | |||
| 										</div> | ||||
| 									{:else} | ||||
| 										<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> | ||||
| 
 | ||||
| 											<div class=" flex justify-start space-x-1"> | ||||
|  | @ -889,4 +899,8 @@ | |||
| 			</div> | ||||
| 		</div> | ||||
| 	{/each} | ||||
| 
 | ||||
| 	{#if bottomPadding} | ||||
| 		<div class=" mb-10" /> | ||||
| 	{/if} | ||||
| {/if} | ||||
|  |  | |||
|  | @ -50,6 +50,10 @@ | |||
| 		messages = []; | ||||
| 	} | ||||
| 
 | ||||
| 	$: if (files) { | ||||
| 		console.log(files); | ||||
| 	} | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		await chatId.set(uuidv4()); | ||||
| 
 | ||||
|  | @ -146,7 +150,15 @@ | |||
| 					...messages | ||||
| 				] | ||||
| 					.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: { | ||||
| 					seed: $settings.seed ?? undefined, | ||||
| 					temperature: $settings.temperature ?? undefined, | ||||
|  | @ -548,6 +560,7 @@ | |||
| 				bind:history | ||||
| 				bind:messages | ||||
| 				bind:autoScroll | ||||
| 				bottomPadding={files.length > 0} | ||||
| 				{sendPrompt} | ||||
| 				{regenerateResponse} | ||||
| 			/> | ||||
|  | @ -555,8 +568,8 @@ | |||
| 	</div> | ||||
| 
 | ||||
| 	<MessageInput | ||||
| 		bind:prompt | ||||
| 		bind:files | ||||
| 		bind:prompt | ||||
| 		bind:autoScroll | ||||
| 		suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [ | ||||
| 			{ | ||||
|  |  | |||
|  | @ -51,17 +51,6 @@ | |||
| 		messages = []; | ||||
| 	} | ||||
| 
 | ||||
| 	// onMount(async () => { | ||||
| 	// 	let chat = await loadChat(); | ||||
| 
 | ||||
| 	// 	await tick(); | ||||
| 	// 	if (chat) { | ||||
| 	// 		loaded = true; | ||||
| 	// 	} else { | ||||
| 	// 		await goto('/'); | ||||
| 	// 	} | ||||
| 	// }); | ||||
| 
 | ||||
| 	$: if ($page.params.id) { | ||||
| 		(async () => { | ||||
| 			let chat = await loadChat(); | ||||
|  | @ -173,7 +162,15 @@ | |||
| 					...messages | ||||
| 				] | ||||
| 					.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: { | ||||
| 					seed: $settings.seed ?? undefined, | ||||
| 					temperature: $settings.temperature ?? undefined, | ||||
|  | @ -579,6 +576,7 @@ | |||
| 					bind:history | ||||
| 					bind:messages | ||||
| 					bind:autoScroll | ||||
| 					bottomPadding={files.length > 0} | ||||
| 					{sendPrompt} | ||||
| 					{regenerateResponse} | ||||
| 				/> | ||||
|  | @ -586,6 +584,7 @@ | |||
| 		</div> | ||||
| 
 | ||||
| 		<MessageInput | ||||
| 			bind:files | ||||
| 			bind:prompt | ||||
| 			bind:autoScroll | ||||
| 			suggestionPrompts={selectedModelfile?.suggestionPrompts ?? [ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy J. Baek
						Timothy J. Baek