Repository: ggml-org/llama.cpp
Pull request: webui: add "delete all conversations" button to import/export tab

- Add 'Delete all conversations' functionality with confirmation dialog
- Add Trash icon and destructive styling for clear visual indication
- Include success summary showing count of deleted conversations

@allozaur let me know what you think about the general approach here.

While testing I noticed I need to clear the current conversion. This is done using chatStore.clearActiveConversation(). A different approach would be to redirect to "?new_chat=true#/" to show the fancy banner.

This is my first time working with TypeScript or Svelte. As a C++ / Rust systems programmer, I appreciate your patience. :grin: Feel free to edit the code any way you like.

UPDATE: Demo video of the improved version:

![webui-delete-all-conversations](https://github.com/user-attachments/assets/092a27f3-ada5-4b82-a839-2d17365518cc)

Changed files:
filename: tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsImportExportTab.svelte
patch: @@ -1,9 +1,11 @@
 <script lang="ts">
-	import { Download, Upload } from '@lucide/svelte';
+	import { Download, Upload, Trash2 } from '@lucide/svelte';
 	import { Button } from '$lib/components/ui/button';
 	import { DialogConversationSelection } from '$lib/components/app';
 	import { createMessageCountMap } from '$lib/utils';
 	import { conversationsStore, conversations } from '$lib/stores/conversations.svelte';
+	import { toast } from 'svelte-sonner';
+	import DialogConfirmation from '$lib/components/app/dialogs/DialogConfirmation.svelte';
 
 	let exportedConversations = $state<DatabaseConversation[]>([]);
 	let importedConversations = $state<DatabaseConversation[]>([]);
@@ -18,11 +20,14 @@
 		[]
 	);
 
+	// Delete functionality state
+	let showDeleteDialog = $state(false);
+
 	async function handleExportClick() {
 		try {
 			const allConversations = conversations();
 			if (allConversations.length === 0) {
-				alert('No conversations to export');
+				toast.info('No conversations to export');
 				return;
 			}
 
@@ -145,6 +150,36 @@
 			alert('Failed to import conversations. Please check the file format.');
 		}
 	}
+
+	async function handleDeleteAllClick() {
+		try {
+			const allConversations = conversations();
+
+			if (allConversations.length === 0) {
+				toast.info('No conversations to delete');
+				return;
+			}
+
+			showDeleteDialog = true;
+		} catch (err) {
+			console.error('Failed to load conversations for deletion:', err);
+			toast.error('Failed to load conversations');
+		}
+	}
+
+	async function handleDeleteAllConfirm() {
+		try {
+			await conversationsStore.deleteAll();
+
+			showDeleteDialog = false;
+		} catch (err) {
+			console.error('Failed to delete conversations:', err);
+		}
+	}
+
+	function handleDeleteAllCancel() {
+		showDeleteDialog = false;
+	}
 </script>
 
 <div class="space-y-6">
@@ -229,6 +264,25 @@
 				</div>
 			{/if}
 		</div>
+
+		<div class="grid border-t border-border/30 pt-4">
+			<h4 class="mb-2 text-sm font-medium text-destructive">Delete All Conversations</h4>
+
+			<p class="mb-4 text-sm text-muted-foreground">
+				Permanently delete all conversations and their messages. This action cannot be undone.
+				Consider exporting your conversations first if you want to keep a backup.
+			</p>
+
+			<Button
+				class="text-destructive-foreground w-full justify-start justify-self-start bg-destructive hover:bg-destructive/80 md:w-auto"
+				onclick={handleDeleteAllClick}
+				variant="destructive"
+			>
+				<Trash2 class="mr-2 h-4 w-4" />
+
+				Delete all conversations
+			</Button>
+		</div>
 	</div>
 </div>
 
@@ -249,3 +303,15 @@
 	onCancel={() => (showImportDialog = false)}
 	onConfirm={handleImportConfirm}
 />
+
+<DialogConfirmation
+	bind:open={showDeleteDialog}
+	title="Delete all conversations"
+	description="Are you sure you want to delete all conversations? This action cannot be undone and will permanently remove all your conversations and messages."
+	confirmText="Delete All"
+	cancelText="Cancel"
+	variant="destructive"
+	icon={Trash2}
+	onConfirm={handleDeleteAllConfirm}
+	onCancel={handleDeleteAllCancel}
+/>

filename: tools/server/webui/src/lib/stores/conversations.svelte.ts
patch: @@ -385,15 +385,37 @@ class ConversationsStore {
 			this.conversations = this.conversations.filter((c) => c.id !== convId);
 
 			if (this.activeConversation?.id === convId) {
-				this.activeConversation = null;
-				this.activeMessages = [];
+				this.clearActiveConversation();
 				await goto(`?new_chat=true#/`);
 			}
 		} catch (error) {
 			console.error('Failed to delete conversation:', error);
 		}
 	}
 
+	/**
+	 * Deletes all conversations and their messages
+	 */
+	async deleteAll(): Promise<void> {
+		try {
+			const allConversations = await DatabaseService.getAllConversations();
+
+			for (const conv of allConversations) {
+				await DatabaseService.deleteConversation(conv.id);
+			}
+
+			this.clearActiveConversation();
+			this.conversations = [];
+
+			toast.success('All conversations deleted');
+
+			await goto(`?new_chat=true#/`);
+		} catch (error) {
+			console.error('Failed to delete all conversations:', error);
+			toast.error('Failed to delete conversations');
+		}
+	}
+
 	// ─────────────────────────────────────────────────────────────────────────────
 	// Import/Export
 	// ─────────────────────────────────────────────────────────────────────────────