[object Object]

← back to Exo

Ciaran/message deletion (#1409)

042999f728b68eae31056d9d99abbc32fe8963be · 2026-02-16 11:46:41 +0000 · ciaranbor

## Motivation

When a user deletes a message during an active streamed generation, it
can cause unexpected behavior. The delete confirmation text was also
misleading — it said "all responses after it" only for user messages,
which didn't accurately describe the behavior (all messages after the
deleted one are removed, regardless of role)

## Changes

- Prevent deletion during streaming: Disabled the delete button and
blocked handleDeleteClick when loading is true, with a visual indication
(dimmed button, cursor-not-allowed, tooltip change)
- Clarified delete confirmation text: Replaced role-specific wording
with a simpler, accurate message:
    - Last message: "Delete this message?"
- Any other message: "Delete this message and all messages after it?"

## Why It Works

Guarding on the loading state at both the click handler and the button's
disabled attribute ensures no deletion can be triggered while a response
is being streamed

## Test Plan

### Manual Testing

- Verify the delete button is visually disabled and non-clickable while
a response is streaming
- Verify the tooltip shows "Cannot delete while generating" during
streaming
  - Verify the last message shows "Delete this message?" confirmation
- Verify non-last messages show "Delete this message and all messages
after it?" confirmation
  - Verify deletion works normally when not streaming

Files touched

Diff

commit 042999f728b68eae31056d9d99abbc32fe8963be
Author: ciaranbor <81697641+ciaranbor@users.noreply.github.com>
Date:   Mon Feb 16 11:46:41 2026 +0000

    Ciaran/message deletion (#1409)
    
    ## Motivation
    
    When a user deletes a message during an active streamed generation, it
    can cause unexpected behavior. The delete confirmation text was also
    misleading — it said "all responses after it" only for user messages,
    which didn't accurately describe the behavior (all messages after the
    deleted one are removed, regardless of role)
    
    ## Changes
    
    - Prevent deletion during streaming: Disabled the delete button and
    blocked handleDeleteClick when loading is true, with a visual indication
    (dimmed button, cursor-not-allowed, tooltip change)
    - Clarified delete confirmation text: Replaced role-specific wording
    with a simpler, accurate message:
        - Last message: "Delete this message?"
    - Any other message: "Delete this message and all messages after it?"
    
    ## Why It Works
    
    Guarding on the loading state at both the click handler and the button's
    disabled attribute ensures no deletion can be triggered while a response
    is being streamed
    
    ## Test Plan
    
    ### Manual Testing
    
    - Verify the delete button is visually disabled and non-clickable while
    a response is streaming
    - Verify the tooltip shows "Cannot delete while generating" during
    streaming
      - Verify the last message shows "Delete this message?" confirmation
    - Verify non-last messages show "Delete this message and all messages
    after it?" confirmation
      - Verify deletion works normally when not streaming
---
 dashboard/src/lib/components/ChatMessages.svelte | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/dashboard/src/lib/components/ChatMessages.svelte b/dashboard/src/lib/components/ChatMessages.svelte
index 46dfe46b..ba5322a7 100644
--- a/dashboard/src/lib/components/ChatMessages.svelte
+++ b/dashboard/src/lib/components/ChatMessages.svelte
@@ -225,6 +225,7 @@
   }
 
   function handleDeleteClick(messageId: string) {
+    if (loading) return;
     deleteConfirmId = messageId;
   }
 
@@ -255,7 +256,7 @@
 </script>
 
 <div class="flex flex-col gap-4 sm:gap-6 {className}">
-  {#each messageList as message (message.id)}
+  {#each messageList as message, i (message.id)}
     <div
       class="group flex {message.role === 'user'
         ? 'justify-end'
@@ -317,9 +318,11 @@
           <!-- Delete confirmation -->
           <div class="bg-red-500/10 border border-red-500/30 rounded-lg p-3">
             <p class="text-xs text-red-400 mb-3">
-              Delete this message{message.role === "user"
-                ? " and all responses after it"
-                : ""}?
+              {#if i === messageList.length - 1}
+                Delete this message?
+              {:else}
+                Delete this message and all messages after it?
+              {/if}
             </p>
             <div class="flex gap-2 justify-end">
               <button
@@ -751,8 +754,13 @@
             <!-- Delete button -->
             <button
               onclick={() => handleDeleteClick(message.id)}
-              class="p-1.5 text-exo-light-gray hover:text-red-400 transition-colors rounded hover:bg-red-500/10 cursor-pointer"
-              title="Delete message"
+              disabled={loading}
+              class="p-1.5 transition-colors rounded {loading
+                ? 'text-exo-light-gray/30 cursor-not-allowed'
+                : 'text-exo-light-gray hover:text-red-400 hover:bg-red-500/10 cursor-pointer'}"
+              title={loading
+                ? "Cannot delete while generating"
+                : "Delete message"}
             >
               <svg
                 class="w-3.5 h-3.5"

← b61dc2eb Prevent image editing without image input (#1410)  ·  back to Exo  ·  fix: PlaceInstanceParams broken field validator 2d8bfc2e →