Pārlūkot izejas kodu

Merge pull request #789 from dannyl1u/feat/delete-message

feat: delete message
Timothy Jaeryang Baek 1 gadu atpakaļ
vecāks
revīzija
d84185f857

+ 119 - 87
src/lib/components/chat/Messages.svelte

@@ -222,6 +222,34 @@
 			scrollToBottom();
 		}, 100);
 	};
+
+	// TODO: change delete behaviour
+	// const deleteMessageAndDescendants = async (messageId: string) => {
+	// 	if (history.messages[messageId]) {
+	// 		history.messages[messageId].deleted = true;
+
+	// 		for (const childId of history.messages[messageId].childrenIds) {
+	// 			await deleteMessageAndDescendants(childId);
+	// 		}
+	// 	}
+	// };
+
+	// const triggerDeleteMessageRecursive = async (messageId: string) => {
+	// 	await deleteMessageAndDescendants(messageId);
+	// 	await updateChatById(localStorage.token, chatId, { history });
+	// 	await chats.set(await getChatList(localStorage.token));
+	// };
+
+	const messageDeleteHandler = async (messageId) => {
+		if (history.messages[messageId]) {
+			history.messages[messageId].deleted = true;
+
+			for (const childId of history.messages[messageId].childrenIds) {
+				history.messages[childId].deleted = true;
+			}
+		}
+		await updateChatById(localStorage.token, chatId, { history });
+	};
 </script>
 
 {#if messages.length == 0}
@@ -230,99 +258,103 @@
 	<div class=" pb-10">
 		{#key chatId}
 			{#each messages as message, messageIdx}
-				<div class=" w-full">
-					<div
-						class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
-							? 'max-w-full'
-							: 'max-w-3xl'} mx-auto rounded-lg group"
-					>
-						{#if message.role === 'user'}
-							<UserMessage
-								user={$user}
-								{message}
-								siblings={message.parentId !== null
-									? history.messages[message.parentId]?.childrenIds ?? []
-									: Object.values(history.messages)
-											.filter((message) => message.parentId === null)
-											.map((message) => message.id) ?? []}
-								{confirmEditMessage}
-								{showPreviousMessage}
-								{showNextMessage}
-								{copyToClipboard}
-							/>
-
-							{#if messages.length - 1 === messageIdx && processing !== ''}
-								<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
-									<div class=" dark:text-blue-100">
-										<svg
-											class=" w-4 h-4 translate-y-[0.5px]"
-											fill="currentColor"
-											viewBox="0 0 24 24"
-											xmlns="http://www.w3.org/2000/svg"
-											><style>
-												.spinner_qM83 {
-													animation: spinner_8HQG 1.05s infinite;
-												}
-												.spinner_oXPr {
-													animation-delay: 0.1s;
-												}
-												.spinner_ZTLf {
-													animation-delay: 0.2s;
-												}
-												@keyframes spinner_8HQG {
-													0%,
-													57.14% {
-														animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
-														transform: translate(0);
+				{#if !message.deleted}
+					<div class=" w-full">
+						<div
+							class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
+								? 'max-w-full'
+								: 'max-w-3xl'} mx-auto rounded-lg group"
+						>
+							{#if message.role === 'user'}
+								<UserMessage
+									on:delete={() => messageDeleteHandler(message.id)}
+									user={$user}
+									{message}
+									isFirstMessage={messageIdx === 0}
+									siblings={message.parentId !== null
+										? history.messages[message.parentId]?.childrenIds ?? []
+										: Object.values(history.messages)
+												.filter((message) => message.parentId === null)
+												.map((message) => message.id) ?? []}
+									{confirmEditMessage}
+									{showPreviousMessage}
+									{showNextMessage}
+									{copyToClipboard}
+								/>
+
+								{#if messages.length - 1 === messageIdx && processing !== ''}
+									<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
+										<div class=" dark:text-blue-100">
+											<svg
+												class=" w-4 h-4 translate-y-[0.5px]"
+												fill="currentColor"
+												viewBox="0 0 24 24"
+												xmlns="http://www.w3.org/2000/svg"
+												><style>
+													.spinner_qM83 {
+														animation: spinner_8HQG 1.05s infinite;
 													}
-													28.57% {
-														animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
-														transform: translateY(-6px);
+													.spinner_oXPr {
+														animation-delay: 0.1s;
 													}
-													100% {
-														transform: translate(0);
+													.spinner_ZTLf {
+														animation-delay: 0.2s;
 													}
-												}
-											</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
-												class="spinner_qM83 spinner_oXPr"
-												cx="12"
-												cy="12"
-												r="2.5"
-											/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg
-										>
-									</div>
-									<div class=" text-sm font-medium">
-										{processing}
+													@keyframes spinner_8HQG {
+														0%,
+														57.14% {
+															animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+															transform: translate(0);
+														}
+														28.57% {
+															animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+															transform: translateY(-6px);
+														}
+														100% {
+															transform: translate(0);
+														}
+													}
+												</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
+													class="spinner_qM83 spinner_oXPr"
+													cx="12"
+													cy="12"
+													r="2.5"
+												/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg
+											>
+										</div>
+										<div class=" text-sm font-medium">
+											{processing}
+										</div>
 									</div>
-								</div>
+								{/if}
+							{:else}
+								<ResponseMessage
+									{message}
+									modelfiles={selectedModelfiles}
+									siblings={history.messages[message.parentId]?.childrenIds ?? []}
+									isLastMessage={messageIdx + 1 === messages.length}
+									{confirmEditResponseMessage}
+									{showPreviousMessage}
+									{showNextMessage}
+									{rateMessage}
+									{copyToClipboard}
+									{continueGeneration}
+									{regenerateResponse}
+									on:save={async (e) => {
+										console.log('save', e);
+
+										const message = e.detail;
+										history.messages[message.id] = message;
+										await updateChatById(localStorage.token, chatId, {
+											messages: messages,
+											history: history
+										});
+									}}
+								/>
 							{/if}
-						{:else}
-							<ResponseMessage
-								{message}
-								modelfiles={selectedModelfiles}
-								siblings={history.messages[message.parentId]?.childrenIds ?? []}
-								isLastMessage={messageIdx + 1 === messages.length}
-								{confirmEditResponseMessage}
-								{showPreviousMessage}
-								{showNextMessage}
-								{rateMessage}
-								{copyToClipboard}
-								{continueGeneration}
-								{regenerateResponse}
-								on:save={async (e) => {
-									console.log('save', e);
-
-									const message = e.detail;
-									history.messages[message.id] = message;
-									await updateChatById(localStorage.token, chatId, {
-										messages: messages,
-										history: history
-									});
-								}}
-							/>
-						{/if}
+						</div>
 					</div>
-				</div>
+				{/if}
 			{/each}
 
 			{#if bottomPadding}

+ 38 - 7
src/lib/components/chat/Messages/UserMessage.svelte

@@ -1,14 +1,17 @@
 <script lang="ts">
 	import dayjs from 'dayjs';
 
-	import { tick } from 'svelte';
+	import { tick, createEventDispatcher } from 'svelte';
 	import Name from './Name.svelte';
 	import ProfileImage from './ProfileImage.svelte';
 	import { modelfiles, settings } from '$lib/stores';
 
+	const dispatch = createEventDispatcher();
+
 	export let user;
 	export let message;
 	export let siblings;
+	export let isFirstMessage: boolean;
 
 	export let confirmEditMessage: Function;
 	export let showPreviousMessage: Function;
@@ -42,6 +45,10 @@
 		edit = false;
 		editedContent = '';
 	};
+
+	const deleteMessageHandler = async () => {
+		dispatch('delete', message.id);
+	};
 </script>
 
 <div class=" flex w-full">
@@ -189,11 +196,11 @@
 				<div class="w-full">
 					<pre id="user-message">{message.content}</pre>
 
-					<div class=" flex justify-start space-x-1">
+					<div class=" flex justify-start space-x-1 text-gray-700 dark:text-gray-500">
 						{#if siblings.length > 1}
 							<div class="flex self-center">
 								<button
-									class="self-center"
+									class="self-center dark:hover:text-white hover:text-black transition"
 									on:click={() => {
 										showPreviousMessage(message);
 									}}
@@ -212,12 +219,12 @@
 									</svg>
 								</button>
 
-								<div class="text-xs font-bold self-center">
+								<div class="text-xs font-bold self-center dark:text-gray-100">
 									{siblings.indexOf(message.id) + 1} / {siblings.length}
 								</div>
 
 								<button
-									class="self-center"
+									class="self-center dark:hover:text-white hover:text-black transition"
 									on:click={() => {
 										showNextMessage(message);
 									}}
@@ -239,7 +246,7 @@
 						{/if}
 
 						<button
-							class="invisible group-hover:visible p-1 rounded dark:hover:text-white transition edit-user-message-button"
+							class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
 							on:click={() => {
 								editMessageHandler();
 							}}
@@ -261,7 +268,7 @@
 						</button>
 
 						<button
-							class="invisible group-hover:visible p-1 rounded dark:hover:text-white transition"
+							class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
 							on:click={() => {
 								copyToClipboard(message.content);
 							}}
@@ -281,6 +288,30 @@
 								/>
 							</svg>
 						</button>
+
+						{#if !isFirstMessage}
+							<button
+								class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
+								on:click={() => {
+									deleteMessageHandler();
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke-width="1.5"
+									stroke="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
+									/>
+								</svg>
+							</button>
+						{/if}
 					</div>
 				</div>
 			{/if}

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

@@ -334,7 +334,7 @@
 						content: $settings.system
 				  }
 				: undefined,
-			...messages
+			...messages.filter(message => !message.deleted)
 		]
 			.filter((message) => message)
 			.map((message, idx, arr) => ({
@@ -540,7 +540,7 @@
 							content: $settings.system
 					  }
 					: undefined,
-				...messages
+			...messages.filter(message => !message.deleted)
 			]
 				.filter((message) => message)
 				.map((message, idx, arr) => ({

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

@@ -348,7 +348,7 @@
 						content: $settings.system
 				  }
 				: undefined,
-			...messages
+			...messages.filter((message) => !message.deleted)
 		]
 			.filter((message) => message)
 			.map((message, idx, arr) => ({
@@ -555,7 +555,7 @@
 							content: $settings.system
 					  }
 					: undefined,
-				...messages
+				...messages.filter((message) => !message.deleted)
 			]
 				.filter((message) => message)
 				.map((message, idx, arr) => ({