diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx index 53ccff13e6d..21ff4756fcf 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx @@ -1,6 +1,6 @@ 'use client' -import { useLayoutEffect, useRef } from 'react' +import { useCallback, useEffect, useLayoutEffect, useRef } from 'react' import { cn } from '@/lib/core/utils/cn' import { MessageActions } from '@/app/workspace/[workspaceId]/components' import { ChatMessageAttachments } from '@/app/workspace/[workspaceId]/home/components/chat-message-attachments' @@ -99,6 +99,43 @@ export function MothershipChat({ const hasMessages = messages.length > 0 const initialScrollDoneRef = useRef(false) + const primedQueueIdRef = useRef(null) + const primeTimerRef = useRef | null>(null) + const messageQueueRef = useRef(messageQueue) + messageQueueRef.current = messageQueue + const onSendQueuedMessageRef = useRef(onSendQueuedMessage) + onSendQueuedMessageRef.current = onSendQueuedMessage + + const clearPrimed = useCallback(() => { + primedQueueIdRef.current = null + if (primeTimerRef.current) { + clearTimeout(primeTimerRef.current) + primeTimerRef.current = null + } + }, []) + + const handleEnterWhileEmpty = useCallback(() => { + const topMessage = messageQueueRef.current[0] + if (!topMessage) return false + + if (primedQueueIdRef.current === topMessage.id) { + clearPrimed() + void onSendQueuedMessageRef.current(topMessage.id) + return true + } + + primedQueueIdRef.current = topMessage.id + if (primeTimerRef.current) clearTimeout(primeTimerRef.current) + primeTimerRef.current = setTimeout(clearPrimed, 3000) + return true + }, [clearPrimed]) + + useEffect(() => { + return () => { + if (primeTimerRef.current) clearTimeout(primeTimerRef.current) + } + }, []) + useLayoutEffect(() => { if (!hasMessages) { initialScrollDoneRef.current = false @@ -197,6 +234,8 @@ export function MothershipChat({ onContextAdd={onContextAdd} editValue={editValue} onEditValueConsumed={onEditValueConsumed} + onEnterWhileEmpty={handleEnterWhileEmpty} + onPrimedDismiss={clearPrimed} /> diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index c44471f019a..e81d47192f0 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -108,6 +108,8 @@ interface UserInputProps { isInitialView?: boolean userId?: string onContextAdd?: (context: ChatContext) => void + onEnterWhileEmpty?: () => boolean + onPrimedDismiss?: () => void } export function UserInput({ @@ -120,6 +122,8 @@ export function UserInput({ isInitialView = true, userId, onContextAdd, + onEnterWhileEmpty, + onPrimedDismiss, }: UserInputProps) { const { workspaceId } = useParams<{ workspaceId: string }>() const { data: workflowsById = {} } = useWorkflowMap(workspaceId) @@ -207,6 +211,10 @@ export function UserInput({ filesRef.current = files const contextRef = useRef(contextManagement) contextRef.current = contextManagement + const onEnterWhileEmptyRef = useRef(onEnterWhileEmpty) + onEnterWhileEmptyRef.current = onEnterWhileEmpty + const isSendingRef = useRef(isSending) + isSendingRef.current = isSending useEffect(() => { return () => { @@ -447,6 +455,9 @@ export function UserInput({ (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault() + if (isSendingRef.current && !valueRef.current.trim() && onEnterWhileEmptyRef.current?.()) { + return + } handleSubmit() return } @@ -539,8 +550,9 @@ export function UserInput({ setValue(newValue) restartRecognition(newValue) + if (newValue.trim()) onPrimedDismiss?.() }, - [restartRecognition] + [restartRecognition, onPrimedDismiss] ) const handleSelectAdjust = useCallback(() => {