diff --git a/web-app/src/containers/AvatarEmoji.tsx b/web-app/src/containers/AvatarEmoji.tsx index 71444b9eb..c041ab175 100644 --- a/web-app/src/containers/AvatarEmoji.tsx +++ b/web-app/src/containers/AvatarEmoji.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { memo } from 'react' /** * Checks if an avatar is a custom image (starts with '/images/') @@ -16,7 +16,7 @@ interface AvatarEmojiProps { textClassName?: string } -export const AvatarEmoji: React.FC = ({ +export const AvatarEmoji: React.FC = memo(({ avatar, imageClassName = 'w-5 h-5 object-contain', textClassName = 'text-base', @@ -27,4 +27,4 @@ export const AvatarEmoji: React.FC = ({ } return {avatar} -} +}) diff --git a/web-app/src/containers/ThinkingBlock.tsx b/web-app/src/containers/ThinkingBlock.tsx index 7a1e7b540..9afc75164 100644 --- a/web-app/src/containers/ThinkingBlock.tsx +++ b/web-app/src/containers/ThinkingBlock.tsx @@ -28,12 +28,12 @@ const useThinkingStore = create((set) => ({ const ThinkingBlock = ({ id, text }: Props) => { const { thinkingState, setThinkingState } = useThinkingStore() - const { streamingContent } = useAppState() + const isStreaming = useAppState((state) => !!state.streamingContent) const { t } = useTranslation() // Check for thinking formats const hasThinkTag = text.includes('') && !text.includes('') const hasAnalysisChannel = text.includes('<|channel|>analysis<|message|>') && !text.includes('<|start|>assistant<|channel|>final<|message|>') - const loading = (hasThinkTag || hasAnalysisChannel) && streamingContent + const loading = (hasThinkTag || hasAnalysisChannel) && isStreaming const isExpanded = thinkingState[id] ?? (loading ? true : false) const handleClick = () => { const newExpandedState = !isExpanded diff --git a/web-app/src/containers/ThreadContent.tsx b/web-app/src/containers/ThreadContent.tsx index 0316ee764..5239bd5d9 100644 --- a/web-app/src/containers/ThreadContent.tsx +++ b/web-app/src/containers/ThreadContent.tsx @@ -87,7 +87,10 @@ export const ThreadContent = memo( [] ) const image = useMemo(() => item.content?.[0]?.image_url, [item]) - const { streamingContent } = useAppState() + // Only check if streaming is happening for this thread, not the content itself + const isStreamingThisThread = useAppState( + (state) => state.streamingContent?.thread_id === item.thread_id + ) const text = useMemo( () => item.content.find((e) => e.type === 'text')?.text?.value ?? '', @@ -360,10 +363,7 @@ export const ThreadContent = memo(
diff --git a/web-app/src/containers/TokenSpeedIndicator.tsx b/web-app/src/containers/TokenSpeedIndicator.tsx index ea9f91be0..704a78995 100644 --- a/web-app/src/containers/TokenSpeedIndicator.tsx +++ b/web-app/src/containers/TokenSpeedIndicator.tsx @@ -1,3 +1,4 @@ +import { memo } from 'react' import { useAppState } from '@/hooks/useAppState' import { toNumber } from '@/utils/number' import { Gauge } from 'lucide-react' @@ -7,11 +8,11 @@ interface TokenSpeedIndicatorProps { streaming?: boolean } -export const TokenSpeedIndicator = ({ +export const TokenSpeedIndicator = memo(({ metadata, streaming, }: TokenSpeedIndicatorProps) => { - const { tokenSpeed } = useAppState() + const tokenSpeed = useAppState((state) => state.tokenSpeed) const persistedTokenSpeed = (metadata?.tokenSpeed as { tokenSpeed: number })?.tokenSpeed || 0 @@ -38,6 +39,6 @@ export const TokenSpeedIndicator = ({ ) -} +}) export default TokenSpeedIndicator diff --git a/web-app/src/hooks/useChat.ts b/web-app/src/hooks/useChat.ts index 45cfde26c..c20989360 100644 --- a/web-app/src/hooks/useChat.ts +++ b/web-app/src/hooks/useChat.ts @@ -33,8 +33,6 @@ import { } from '@/utils/reasoning' export const useChat = () => { - const prompt = usePrompt((state) => state.prompt) - const setPrompt = usePrompt((state) => state.setPrompt) const tools = useAppState((state) => state.tools) const updateTokenSpeed = useAppState((state) => state.updateTokenSpeed) const resetTokenSpeed = useAppState((state) => state.resetTokenSpeed) @@ -88,12 +86,14 @@ export const useChat = () => { let currentThread = retrieveThread() if (!currentThread) { + // Get prompt directly from store when needed + const currentPrompt = usePrompt.getState().prompt currentThread = await createThread( { id: selectedModel?.id ?? defaultModel(selectedProvider), provider: selectedProvider, }, - prompt, + currentPrompt, selectedAssistant ) router.navigate({ @@ -104,7 +104,6 @@ export const useChat = () => { return currentThread }, [ createThread, - prompt, retrieveThread, router, selectedModel?.id, @@ -241,7 +240,7 @@ export const useChat = () => { if (troubleshooting) addMessage(newUserThreadContent(activeThread.id, message, attachments)) updateThreadTimestamp(activeThread.id) - setPrompt('') + usePrompt.getState().setPrompt('') try { if (selectedModel?.id) { updateLoadingModel(true) @@ -554,7 +553,6 @@ export const useChat = () => { updateStreamingContent, addMessage, updateThreadTimestamp, - setPrompt, selectedModel, currentAssistant, tools,