aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHardik Vora <[email protected]>2025-11-18 15:51:23 -0800
committerGitHub <[email protected]>2025-11-18 15:51:23 -0800
commitd8e7c381a09317905d8d58900d5b47e6342aa8d5 (patch)
tree01e0b90750753997c4fc9c441839cadfbf729795
parentfix: document source (#580) (diff)
downloadsupermemory-d8e7c381a09317905d8d58900d5b47e6342aa8d5.tar.xz
supermemory-d8e7c381a09317905d8d58900d5b47e6342aa8d5.zip
Fix: Prevent multiple prompts while AI response is generated (fixes #538) (#583)
-rw-r--r--apps/web/components/views/chat/chat-messages.tsx73
1 files changed, 55 insertions, 18 deletions
diff --git a/apps/web/components/views/chat/chat-messages.tsx b/apps/web/components/views/chat/chat-messages.tsx
index 0c795ec0..60a82132 100644
--- a/apps/web/components/views/chat/chat-messages.tsx
+++ b/apps/web/components/views/chat/chat-messages.tsx
@@ -12,6 +12,7 @@ import {
Copy,
RotateCcw,
X,
+ Square
} from "lucide-react"
import { useCallback, useEffect, useRef, useState } from "react"
import { toast } from "sonner"
@@ -432,11 +433,37 @@ export function ChatMessages() {
shouldGenerateTitleRef.current = !hasTitle
}, [getCurrentChat])
+ /**
+ * Handles sending a message from the input area.
+ * - Prevents sending during submitted (shows toast)
+ * - Stops streaming when active
+ * - Validates non-empty input (shows toast)
+ * Returns true when a message is sent.
+ */
+ const handleSendMessage = useCallback(() => {
+ if (status === "submitted") {
+ toast.warning("Please wait for the current response to complete", {
+ id: "wait-for-response",
+ })
+ return false
+ }
+ if (status === "streaming") {
+ stop()
+ return false
+ }
+ if (!input.trim()) {
+ toast.warning("Please enter a message", { id: "empty-message" })
+ return false
+ }
+ sendMessage({ text: input })
+ setInput("")
+ return true
+ }, [status, input, sendMessage, stop])
+
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
- sendMessage({ text: input })
- setInput("")
+ handleSendMessage()
}
}
@@ -657,16 +684,10 @@ export function ChatMessages() {
className="flex flex-col items-end gap-3 border border-border rounded-[22px] p-3 relative shadow-lg dark:shadow-2xl"
onSubmit={(e) => {
e.preventDefault()
- if (status === "submitted") return
- if (status === "streaming") {
- stop()
- return
- }
- if (input.trim()) {
+ const sent = handleSendMessage()
+ if (sent) {
enableAutoScroll()
scrollToBottom("auto")
- sendMessage({ text: input })
- setInput("")
}
}}
>
@@ -674,6 +695,8 @@ export function ChatMessages() {
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
+ aria-busy={status === "streaming" || status === "submitted"}
+ aria-disabled={status === "submitted"}
placeholder="Ask your follow-up question..."
className="w-full text-foreground placeholder:text-muted-foreground rounded-md outline-none resize-none text-base leading-relaxed px-3 py-3 bg-transparent"
rows={3}
@@ -689,14 +712,28 @@ export function ChatMessages() {
{modelNames[selectedModel]}
</span>
</div>
- <Button
- type="submit"
- disabled={!input.trim()}
- className="text-primary-foreground rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed bg-primary hover:bg-primary/90"
- size="icon"
- >
- <ArrowUp className="size-4" />
- </Button>
+ {status === "streaming" || status === "submitted" ? (
+ <Button
+ onClick={() => stop()}
+ aria-label="Stop generation"
+ className="rounded-xl"
+ variant="destructive"
+ size="icon"
+ type="button"
+ >
+ <Square className="size-4" />
+ </Button>
+ ) : (
+ <Button
+ type="submit"
+ aria-label="Send message"
+ disabled={!input.trim()}
+ className="text-primary-foreground rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed bg-primary hover:bg-primary/90"
+ size="icon"
+ >
+ <ArrowUp className="size-4" />
+ </Button>
+ )}
</div>
</form>
</div>