Skip to content

fix(chat): remove confirmation modal when closing chat tab#1263

Merged
arnestrickmann merged 1 commit intomainfrom
emdash/remove-warning-when-closing-chat-tab-5wn
Mar 4, 2026
Merged

fix(chat): remove confirmation modal when closing chat tab#1263
arnestrickmann merged 1 commit intomainfrom
emdash/remove-warning-when-closing-chat-tab-5wn

Conversation

@arnestrickmann
Copy link
Contributor

Summary

  • Remove the DeleteChatModal confirmation dialog that appeared when closing a chat tab
  • Inline the chat deletion logic directly into handleCloseChat, making tab close instant
  • Delete the now-unused DeleteChatModal component

Motivation

The confirmation modal added unnecessary friction when closing chat tabs. Since chats can be easily recreated and this is a lightweight action, the extra confirmation step was more disruptive than helpful.

@vercel
Copy link

vercel bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Mar 3, 2026 11:59pm

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 4, 2026

Greptile Summary

This PR removes the DeleteChatModal confirmation dialog and inlines the chat deletion logic directly into handleCloseChat, making tab close instant. The change is well-motivated and the code refactor is clean, but two concerns were found:

  • No busy-state guard: The handleCloseChat function does not check busyByConversationId[conversationId] before proceeding. Clicking ✕ on a tab that is actively running will immediately dispose the terminal process and permanently delete the conversation and all its messages — with no warning. The old confirmation modal at least surfaced the irreversibility of the action.
  • Unguarded async operations: The async DB calls (deleteConversation, getConversations, setActiveConversation) are not wrapped in a try/catch. A failure after terminalSessionRegistry.dispose() leaves the terminal gone but the conversation still in the DB, and the error surfaces as an unhandled promise rejection since the callers don't await or .catch() the result.

Confidence Score: 3/5

  • Safe to merge if the team accepts instant deletion of busy chats; the busy-state guard omission is a meaningful UX regression that can cause silent data loss.
  • The inlining of logic is correct and the dead code is cleanly removed, but the missing busy-state check introduces a regression where an actively-running chat can be irreversibly deleted mid-generation with no confirmation or warning. The lack of error handling around the DB operations also risks leaving app state inconsistent on failure.
  • src/renderer/components/ChatInterface.tsx — specifically the handleCloseChat callback around lines 517–560.

Important Files Changed

Filename Overview
src/renderer/components/ChatInterface.tsx Inlines deletion logic from the removed modal into handleCloseChat; however, lacks a busy-state guard (chat can be deleted while actively running) and has no error handling around the async DB operations, which can leave state inconsistent on failure.
src/renderer/components/DeleteChatModal.tsx File deleted entirely as part of the modal removal — clean deletion with no dangling references.

Sequence Diagram

sequenceDiagram
    actor User
    participant Tab as Chat Tab (UI)
    participant HC as handleCloseChat
    participant TR as terminalSessionRegistry
    participant DB as rpc.db
    participant State as React State

    User->>Tab: Click ✕ (close button)
    Tab->>HC: handleCloseChat(conversationId)
    alt conversations.length <= 1
        HC-->>User: toast "Cannot Close"
    else
        Note over HC: ⚠️ No busy-state check here
        HC->>TR: dispose(terminalId)
        HC->>DB: deleteConversation(conversationId)
        HC->>DB: getConversations(taskId)
        DB-->>HC: updatedConversations
        HC->>State: setConversations(updatedConversations)
        alt deleted chat was active
            HC->>DB: setActiveConversation(newActive)
            HC->>State: setActiveConversationId / setAgent
        end
        HC->>Window: dispatchEvent('emdash:conversations-changed')
    end
Loading

Last reviewed commit: 9cdd14c

Comment on lines 517 to 526
@@ -528,52 +525,39 @@ const ChatInterface: React.FC<Props> = ({
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No guard against closing a busy/running chat

The handleCloseChat function now immediately disposes the terminal and deletes the conversation without checking whether the chat is actively processing a request. When busyByConversationId[conversationId] === true, clicking the ✕ button will:

  1. Dispose the running terminal process mid-generation
  2. Permanently delete all conversation messages from the DB

Before this PR, the confirmation modal at least surfaced the warning "This will permanently delete this chat and all its messages. This action cannot be undone." — giving the user a chance to cancel. Now, there is no protection at all for a busy chat.

Consider adding a busy-state guard at the top of the function:

Suggested change
const handleCloseChat = useCallback(
async (conversationId: string) => {
if (conversations.length <= 1) {
toast({
title: 'Cannot Close',
description: 'Cannot close the last chat',
variant: 'destructive',
});
return;
}
if (busyByConversationId[conversationId]) {
toast({
title: 'Cannot Close',
description: 'Cannot close a chat while it is running',
variant: 'destructive',
});
return;
}

Comment on lines +528 to +557
// Dispose the terminal for this chat
const convToDelete = conversations.find((c) => c.id === conversationId);
const convAgent = (convToDelete?.provider || agent) as Agent;
const terminalToDispose = makePtyId(convAgent, 'chat', conversationId);
terminalSessionRegistry.dispose(terminalToDispose);

await rpc.db.deleteConversation(conversationId);

// Reload conversations
const updatedConversations = await rpc.db.getConversations(task.id);
setConversations(updatedConversations);
// Switch to another chat if we deleted the active one
if (conversationId === activeConversationId && updatedConversations.length > 0) {
const newActive = updatedConversations[0];
await rpc.db.setActiveConversation({
taskId: task.id,
conversationId: newActive.id,
});
setActiveConversationId(newActive.id);
// Update provider if needed
if (newActive.provider) {
setAgent(newActive.provider as Agent);
}
}
}

try {
window.dispatchEvent(
new CustomEvent('emdash:conversations-changed', { detail: { taskId: task.id } })
);
} catch {}

// Clear the state
setChatToDelete(null);
setShowDeleteChatModal(false);
}, [chatToDelete, conversations, agent, task.id, activeConversationId]);
try {
window.dispatchEvent(
new CustomEvent('emdash:conversations-changed', { detail: { taskId: task.id } })
);
} catch {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No error handling around async DB operations

If rpc.db.deleteConversation or rpc.db.getConversations throws, the function exits after the terminal has already been disposed — leaving the conversation still in the DB while its terminal session is gone. Additionally, since handleCloseChat is now async and is called from click/keyboard event handlers and a window event listener without .catch(), any rejection becomes an unhandled promise rejection.

Wrapping the body in a try/catch (and reverting the terminal dispose on failure) would keep state consistent:

const terminalToDispose = makePtyId(convAgent, 'chat', conversationId);
terminalSessionRegistry.dispose(terminalToDispose);
try {
  await rpc.db.deleteConversation(conversationId);
  // ... rest of logic
} catch (err) {
  // Re-register or notify; at minimum surface the error
  toast({ title: 'Error', description: 'Failed to close chat', variant: 'destructive' });
}

@arnestrickmann arnestrickmann merged commit 44a728e into main Mar 4, 2026
5 checks passed
@arnestrickmann arnestrickmann deleted the emdash/remove-warning-when-closing-chat-tab-5wn branch March 5, 2026 00:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant