From b51b24955c3c4d54a6aafc6eb007aa4c49a2f114 Mon Sep 17 00:00:00 2001 From: Marc Beaupre Date: Wed, 31 Dec 2025 17:47:42 -0500 Subject: [PATCH] fix(chat): clear input immediately after send to prevent duplicate messages Two issues were causing the input field to retain text after sending: 1. ChatComposer's NSViewRepresentable was skipping all updates while the text view was first responder. Now it allows clearing (empty binding) even during editing, only skipping other updates to avoid cursor jumps. 2. ChatViewModel cleared input after awaiting the network response, leaving text visible during the round trip. Now clears immediately after capturing the message content, before the async send. Together these prevent users from accidentally re-sending messages when the input appeared unchanged after pressing Enter. --- .../ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift | 6 +++++- .../ClawdisKit/Sources/ClawdisChatUI/ChatViewModel.swift | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift b/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift index 41b1d792b..bc6b08db4 100644 --- a/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift +++ b/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatComposer.swift @@ -412,7 +412,11 @@ private struct ChatComposerTextView: NSViewRepresentable { } let isEditing = scrollView.window?.firstResponder == textView - if isEditing { return } + + // Always allow clearing the text (e.g. after send), even while editing. + // Only skip other updates while editing to avoid cursor jumps. + let shouldClear = self.text.isEmpty && !textView.string.isEmpty + if isEditing && !shouldClear { return } if textView.string != self.text { context.coordinator.isProgrammaticUpdate = true diff --git a/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatViewModel.swift b/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatViewModel.swift index 087ef912c..14ce2091a 100644 --- a/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatViewModel.swift +++ b/apps/shared/ClawdisKit/Sources/ClawdisChatUI/ChatViewModel.swift @@ -243,6 +243,10 @@ public final class ClawdisChatViewModel { content: userContent, timestamp: Date().timeIntervalSince1970 * 1000)) + // Clear input immediately for responsive UX (before network await) + self.input = "" + self.attachments = [] + do { let response = try await self.transport.sendMessage( sessionKey: self.sessionKey, @@ -261,8 +265,6 @@ public final class ClawdisChatViewModel { chatUILogger.error("chat.send failed \(error.localizedDescription, privacy: .public)") } - self.input = "" - self.attachments = [] self.isSending = false }