diff --git a/web-app/src/containers/ChatInput.tsx b/web-app/src/containers/ChatInput.tsx index ec7fefcf9..1ed69df96 100644 --- a/web-app/src/containers/ChatInput.tsx +++ b/web-app/src/containers/ChatInput.tsx @@ -3,6 +3,7 @@ import TextareaAutosize from 'react-textarea-autosize' import { cn } from '@/lib/utils' import { usePrompt } from '@/hooks/usePrompt' +import { useThreads } from '@/hooks/useThreads' import { useCallback, useEffect, useRef, useState } from 'react' import { Button } from '@/components/ui/button' import { ArrowRight } from 'lucide-react' @@ -47,6 +48,7 @@ const ChatInput = ({ const { streamingContent, updateTools, abortControllers, loadingModel } = useAppState() const { prompt, setPrompt } = usePrompt() + const { currentThreadId } = useThreads() const { t } = useTranslation() const { spellCheckChatInput } = useGeneralSetting() const { tokenSpeed } = useAppState() @@ -95,12 +97,30 @@ const ChatInput = ({ return unsubscribe }, [updateTools]) + // Focus when component mounts useEffect(() => { if (textareaRef.current) { textareaRef.current.focus() } }, []) + // Focus when thread changes + useEffect(() => { + if (textareaRef.current) { + textareaRef.current.focus() + } + }, [currentThreadId]) + + // Focus when streaming content finishes + useEffect(() => { + if (!streamingContent && textareaRef.current) { + // Small delay to ensure UI has updated + setTimeout(() => { + textareaRef.current?.focus() + }, 10) + } + }, [streamingContent]) + const stopStreaming = useCallback( (threadId: string) => { abortControllers[threadId]?.abort() @@ -227,7 +247,9 @@ const ChatInput = ({ {showSpeedToken && (