mirror of
				https://github.com/open-webui/open-webui
				synced 2025-06-26 18:26:48 +00:00 
			
		
		
		
	feat: edit message files
This commit is contained in:
		
							parent
							
								
									ba69c751ca
								
							
						
					
					
						commit
						30cc51ec82
					
				@ -255,7 +255,7 @@
 | 
			
		||||
		await updateChat();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const editMessage = async (messageId, content, submit = true) => {
 | 
			
		||||
	const editMessage = async (messageId, { content, files }, submit = true) => {
 | 
			
		||||
		if (history.messages[messageId].role === 'user') {
 | 
			
		||||
			if (submit) {
 | 
			
		||||
				// New user message
 | 
			
		||||
@ -268,7 +268,7 @@
 | 
			
		||||
					childrenIds: [],
 | 
			
		||||
					role: 'user',
 | 
			
		||||
					content: userPrompt,
 | 
			
		||||
					...(history.messages[messageId].files && { files: history.messages[messageId].files }),
 | 
			
		||||
					...(files && { files: files }),
 | 
			
		||||
					models: selectedModels,
 | 
			
		||||
					timestamp: Math.floor(Date.now() / 1000) // Unix epoch
 | 
			
		||||
				};
 | 
			
		||||
@ -290,6 +290,7 @@
 | 
			
		||||
			} else {
 | 
			
		||||
				// Edit user message
 | 
			
		||||
				history.messages[messageId].content = content;
 | 
			
		||||
				history.messages[messageId].files = files;
 | 
			
		||||
				await updateChat();
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
 | 
			
		||||
@ -377,7 +377,7 @@
 | 
			
		||||
 | 
			
		||||
	const editMessageConfirmHandler = async () => {
 | 
			
		||||
		const messageContent = postprocessAfterEditing(editedContent ? editedContent : '');
 | 
			
		||||
		editMessage(message.id, messageContent, false);
 | 
			
		||||
		editMessage(message.id, { content: messageContent }, false);
 | 
			
		||||
 | 
			
		||||
		edit = false;
 | 
			
		||||
		editedContent = '';
 | 
			
		||||
@ -388,7 +388,7 @@
 | 
			
		||||
	const saveAsCopyHandler = async () => {
 | 
			
		||||
		const messageContent = postprocessAfterEditing(editedContent ? editedContent : '');
 | 
			
		||||
 | 
			
		||||
		editMessage(message.id, messageContent);
 | 
			
		||||
		editMessage(message.id, { content: messageContent });
 | 
			
		||||
 | 
			
		||||
		edit = false;
 | 
			
		||||
		editedContent = '';
 | 
			
		||||
 | 
			
		||||
@ -43,6 +43,8 @@
 | 
			
		||||
 | 
			
		||||
	let edit = false;
 | 
			
		||||
	let editedContent = '';
 | 
			
		||||
	let editedFiles = [];
 | 
			
		||||
 | 
			
		||||
	let messageEditTextAreaElement: HTMLTextAreaElement;
 | 
			
		||||
 | 
			
		||||
	let message = JSON.parse(JSON.stringify(history.messages[messageId]));
 | 
			
		||||
@ -62,6 +64,7 @@
 | 
			
		||||
	const editMessageHandler = async () => {
 | 
			
		||||
		edit = true;
 | 
			
		||||
		editedContent = message.content;
 | 
			
		||||
		editedFiles = message.files;
 | 
			
		||||
 | 
			
		||||
		await tick();
 | 
			
		||||
 | 
			
		||||
@ -74,15 +77,17 @@
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const editMessageConfirmHandler = async (submit = true) => {
 | 
			
		||||
		editMessage(message.id, editedContent, submit);
 | 
			
		||||
		editMessage(message.id, { content: editedContent, files: editedFiles }, submit);
 | 
			
		||||
 | 
			
		||||
		edit = false;
 | 
			
		||||
		editedContent = '';
 | 
			
		||||
		editedFiles = [];
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const cancelEditMessage = () => {
 | 
			
		||||
		edit = false;
 | 
			
		||||
		editedContent = '';
 | 
			
		||||
		editedFiles = [];
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const deleteMessageHandler = async () => {
 | 
			
		||||
@ -141,30 +146,90 @@
 | 
			
		||||
		{/if}
 | 
			
		||||
 | 
			
		||||
		<div class="chat-{message.role} w-full min-w-full markdown-prose">
 | 
			
		||||
			{#if message.files}
 | 
			
		||||
				<div class="mt-2.5 mb-1 w-full flex flex-col justify-end overflow-x-auto gap-1 flex-wrap">
 | 
			
		||||
					{#each message.files as file}
 | 
			
		||||
						<div class={($settings?.chatBubble ?? true) ? 'self-end' : ''}>
 | 
			
		||||
							{#if file.type === 'image'}
 | 
			
		||||
								<Image src={file.url} imageClassName=" max-h-96 rounded-lg" />
 | 
			
		||||
							{:else}
 | 
			
		||||
								<FileItem
 | 
			
		||||
									item={file}
 | 
			
		||||
									url={file.url}
 | 
			
		||||
									name={file.name}
 | 
			
		||||
									type={file.type}
 | 
			
		||||
									size={file?.size}
 | 
			
		||||
									colorClassName="bg-white dark:bg-gray-850 "
 | 
			
		||||
								/>
 | 
			
		||||
							{/if}
 | 
			
		||||
						</div>
 | 
			
		||||
					{/each}
 | 
			
		||||
				</div>
 | 
			
		||||
			{#if edit !== true}
 | 
			
		||||
				{#if message.files}
 | 
			
		||||
					<div class="mt-2.5 mb-1 w-full flex flex-col justify-end overflow-x-auto gap-1 flex-wrap">
 | 
			
		||||
						{#each message.files as file}
 | 
			
		||||
							<div class={($settings?.chatBubble ?? true) ? 'self-end' : ''}>
 | 
			
		||||
								{#if file.type === 'image'}
 | 
			
		||||
									<Image src={file.url} imageClassName=" max-h-96 rounded-lg" />
 | 
			
		||||
								{:else}
 | 
			
		||||
									<FileItem
 | 
			
		||||
										item={file}
 | 
			
		||||
										url={file.url}
 | 
			
		||||
										name={file.name}
 | 
			
		||||
										type={file.type}
 | 
			
		||||
										size={file?.size}
 | 
			
		||||
										colorClassName="bg-white dark:bg-gray-850 "
 | 
			
		||||
									/>
 | 
			
		||||
								{/if}
 | 
			
		||||
							</div>
 | 
			
		||||
						{/each}
 | 
			
		||||
					</div>
 | 
			
		||||
				{/if}
 | 
			
		||||
			{/if}
 | 
			
		||||
 | 
			
		||||
			{#if message.content !== ''}
 | 
			
		||||
				{#if edit === true}
 | 
			
		||||
					<div class=" w-full bg-gray-50 dark:bg-gray-800 rounded-3xl px-5 py-3 mb-2">
 | 
			
		||||
						{#if editedFiles.length > 0}
 | 
			
		||||
							<div class="flex items-center flex-wrap gap-2">
 | 
			
		||||
								{#each editedFiles as file, fileIdx}
 | 
			
		||||
									{#if file.type === 'image'}
 | 
			
		||||
										<div class=" relative group">
 | 
			
		||||
											<div class="relative flex items-center">
 | 
			
		||||
												<Image
 | 
			
		||||
													src={file.url}
 | 
			
		||||
													alt="input"
 | 
			
		||||
													imageClassName=" size-14 rounded-xl object-cover"
 | 
			
		||||
												/>
 | 
			
		||||
											</div>
 | 
			
		||||
											<div class=" absolute -top-1 -right-1">
 | 
			
		||||
												<button
 | 
			
		||||
													class=" bg-white text-black border border-white rounded-full group-hover:visible invisible transition"
 | 
			
		||||
													type="button"
 | 
			
		||||
													on:click={() => {
 | 
			
		||||
														editedFiles.splice(fileIdx, 1);
 | 
			
		||||
 | 
			
		||||
														editedFiles = editedFiles;
 | 
			
		||||
													}}
 | 
			
		||||
												>
 | 
			
		||||
													<svg
 | 
			
		||||
														xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
														viewBox="0 0 20 20"
 | 
			
		||||
														fill="currentColor"
 | 
			
		||||
														class="size-4"
 | 
			
		||||
													>
 | 
			
		||||
														<path
 | 
			
		||||
															d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
 | 
			
		||||
														/>
 | 
			
		||||
													</svg>
 | 
			
		||||
												</button>
 | 
			
		||||
											</div>
 | 
			
		||||
										</div>
 | 
			
		||||
									{:else}
 | 
			
		||||
										<FileItem
 | 
			
		||||
											item={file}
 | 
			
		||||
											name={file.name}
 | 
			
		||||
											type={file.type}
 | 
			
		||||
											size={file?.size}
 | 
			
		||||
											loading={file.status === 'uploading'}
 | 
			
		||||
											dismissible={true}
 | 
			
		||||
											edit={true}
 | 
			
		||||
											on:dismiss={async () => {
 | 
			
		||||
												editedFiles.splice(fileIdx, 1);
 | 
			
		||||
 | 
			
		||||
												editedFiles = editedFiles;
 | 
			
		||||
											}}
 | 
			
		||||
											on:click={() => {
 | 
			
		||||
												console.log(file);
 | 
			
		||||
											}}
 | 
			
		||||
										/>
 | 
			
		||||
									{/if}
 | 
			
		||||
								{/each}
 | 
			
		||||
							</div>
 | 
			
		||||
						{/if}
 | 
			
		||||
 | 
			
		||||
						<div class="max-h-96 overflow-auto">
 | 
			
		||||
							<textarea
 | 
			
		||||
								id="message-edit-{message.id}"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user