diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components/thinking-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components/thinking-block.tsx index fbb7065f9..234b90b00 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components/thinking-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components/thinking-block.tsx @@ -46,12 +46,16 @@ interface SmoothThinkingTextProps { */ const SmoothThinkingText = memo( ({ content, isStreaming }: SmoothThinkingTextProps) => { - const [displayedContent, setDisplayedContent] = useState('') + // Initialize with full content when not streaming to avoid flash on page load + const [displayedContent, setDisplayedContent] = useState(() => + isStreaming ? '' : content + ) const [showGradient, setShowGradient] = useState(false) const contentRef = useRef(content) const textRef = useRef(null) const rafRef = useRef(null) - const indexRef = useRef(0) + // Initialize index based on streaming state + const indexRef = useRef(isStreaming ? 0 : content.length) const lastFrameTimeRef = useRef(0) const isAnimatingRef = useRef(false) diff --git a/apps/sim/stores/panel/copilot/store.ts b/apps/sim/stores/panel/copilot/store.ts index 64d1d3e7b..95f7375fd 100644 --- a/apps/sim/stores/panel/copilot/store.ts +++ b/apps/sim/stores/panel/copilot/store.ts @@ -422,7 +422,8 @@ function abortAllInProgressTools(set: any, get: () => CopilotStore) { * Loads messages from DB for UI rendering. * Messages are stored exactly as they render, so we just need to: * 1. Register client tool instances for any tool calls - * 2. Return the messages as-is + * 2. Clear any streaming flags (messages loaded from DB are never actively streaming) + * 3. Return the messages */ function normalizeMessagesForUI(messages: CopilotMessage[]): CopilotMessage[] { try { @@ -438,23 +439,51 @@ function normalizeMessagesForUI(messages: CopilotMessage[]): CopilotMessage[] { } } - // Register client tool instances for all tool calls so they can be looked up + // Register client tool instances and clear streaming flags for all tool calls for (const message of messages) { if (message.contentBlocks) { for (const block of message.contentBlocks as any[]) { if (block?.type === 'tool_call' && block.toolCall) { registerToolCallInstances(block.toolCall) + clearStreamingFlags(block.toolCall) } } } } - // Return messages as-is - they're already in the correct format for rendering + // Return messages - they're already in the correct format for rendering return messages } catch { return messages } } +/** + * Recursively clears streaming flags from a tool call and its nested subagent tool calls. + * This ensures messages loaded from DB don't appear to be streaming. + */ +function clearStreamingFlags(toolCall: any): void { + if (!toolCall) return + + // Clear the subAgentStreaming flag + if ('subAgentStreaming' in toolCall) { + toolCall.subAgentStreaming = false + } + + // Clear nested subagent tool calls + if (Array.isArray(toolCall.subAgentBlocks)) { + for (const block of toolCall.subAgentBlocks) { + if (block?.type === 'subagent_tool_call' && block.toolCall) { + clearStreamingFlags(block.toolCall) + } + } + } + if (Array.isArray(toolCall.subAgentToolCalls)) { + for (const subTc of toolCall.subAgentToolCalls) { + clearStreamingFlags(subTc) + } + } +} + /** * Recursively registers client tool instances for a tool call and its nested subagent tool calls. */