Skip to content

Commit c0d79b1

Browse files
committed
fix(copilot): tool call spacing and shimmer/actions on previous messages
1 parent 003f52d commit c0d79b1

File tree

2 files changed

+54
-31
lines changed

2 files changed

+54
-31
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/copilot-message.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
import { CopilotMarkdownRenderer } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/components/markdown-renderer'
2020
import {
2121
useCheckpointManagement,
22-
useMessageContentAnalysis,
2322
useMessageEditing,
2423
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/hooks'
2524
import { UserInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/user-input'
@@ -199,7 +198,7 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
199198
[sendMessage]
200199
)
201200

202-
const { hasVisibleContent } = useMessageContentAnalysis({ message })
201+
const isActivelyStreaming = isLastMessage && isStreaming
203202

204203
const memoizedContentBlocks = useMemo(() => {
205204
if (!message.contentBlocks || message.contentBlocks.length === 0) {
@@ -215,13 +214,16 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
215214

216215
if (!cleanBlockContent.trim()) return null
217216

218-
const shouldUseSmoothing = isStreaming && isLastTextBlock
217+
const shouldUseSmoothing = isActivelyStreaming && isLastTextBlock
219218
const blockKey = `text-${index}-${block.timestamp || index}`
220219

221220
return (
222221
<div key={blockKey} className='w-full max-w-full'>
223222
{shouldUseSmoothing ? (
224-
<SmoothStreamingText content={cleanBlockContent} isStreaming={isStreaming} />
223+
<SmoothStreamingText
224+
content={cleanBlockContent}
225+
isStreaming={isActivelyStreaming}
226+
/>
225227
) : (
226228
<CopilotMarkdownRenderer content={cleanBlockContent} />
227229
)}
@@ -237,7 +239,7 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
237239
<div key={blockKey} className='w-full'>
238240
<ThinkingBlock
239241
content={block.content}
240-
isStreaming={isStreaming}
242+
isStreaming={isActivelyStreaming}
241243
hasFollowingContent={hasFollowingContent}
242244
hasSpecialTags={hasSpecialTags}
243245
/>
@@ -249,13 +251,17 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
249251

250252
return (
251253
<div key={blockKey}>
252-
<ToolCall toolCallId={block.toolCall.id} toolCall={block.toolCall} />
254+
<ToolCall
255+
toolCallId={block.toolCall.id}
256+
toolCall={block.toolCall}
257+
isCurrentMessage={isLastMessage}
258+
/>
253259
</div>
254260
)
255261
}
256262
return null
257263
})
258-
}, [message.contentBlocks, isStreaming, parsedTags])
264+
}, [message.contentBlocks, isActivelyStreaming, parsedTags, isLastMessage])
259265

260266
if (isUser) {
261267
return (

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/tool-call/tool-call.tsx

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,8 @@ interface ToolCallProps {
455455
toolCallId?: string
456456
/** Callback when tool call state changes */
457457
onStateChange?: (state: any) => void
458+
/** Whether this tool call is from the current/latest message. Controls shimmer and action buttons. */
459+
isCurrentMessage?: boolean
458460
}
459461

460462
/** Props for the ShimmerOverlayText component */
@@ -711,6 +713,8 @@ function getSubagentCompletionLabel(toolName: string): string {
711713

712714
/**
713715
* Renders subagent blocks as thinking text within regular tool calls.
716+
* @param blocks - The subagent content blocks to render
717+
* @param isStreaming - Whether streaming animations should be shown (caller should pre-compute currentMessage check)
714718
*/
715719
function SubAgentThinkingContent({
716720
blocks,
@@ -768,16 +772,20 @@ const COLLAPSIBLE_SUBAGENTS = new Set(['plan', 'debug', 'research'])
768772
const SubagentContentRenderer = memo(function SubagentContentRenderer({
769773
toolCall,
770774
shouldCollapse,
775+
isCurrentMessage = true,
771776
}: {
772777
toolCall: CopilotToolCall
773778
shouldCollapse: boolean
779+
/** Whether this is from the current/latest message. Controls shimmer animations. */
780+
isCurrentMessage?: boolean
774781
}) {
775782
const [isExpanded, setIsExpanded] = useState(true)
776783
const [duration, setDuration] = useState(0)
777784
const startTimeRef = useRef<number>(Date.now())
778785
const wasStreamingRef = useRef(false)
779786

780-
const isStreaming = !!toolCall.subAgentStreaming
787+
// Only show streaming animations for current message
788+
const isStreaming = isCurrentMessage && !!toolCall.subAgentStreaming
781789

782790
useEffect(() => {
783791
if (isStreaming && !wasStreamingRef.current) {
@@ -871,7 +879,11 @@ const SubagentContentRenderer = memo(function SubagentContentRenderer({
871879
}
872880
return (
873881
<div key={`tool-${segment.block.toolCall.id || index}`}>
874-
<ToolCall toolCallId={segment.block.toolCall.id} toolCall={segment.block.toolCall} />
882+
<ToolCall
883+
toolCallId={segment.block.toolCall.id}
884+
toolCall={segment.block.toolCall}
885+
isCurrentMessage={isCurrentMessage}
886+
/>
875887
</div>
876888
)
877889
}
@@ -909,7 +921,7 @@ const SubagentContentRenderer = memo(function SubagentContentRenderer({
909921
<div
910922
className={clsx(
911923
'overflow-hidden transition-all duration-150 ease-out',
912-
isExpanded ? 'mt-1.5 max-h-[5000px] opacity-100' : 'max-h-0 opacity-0'
924+
isExpanded ? 'mt-1.5 max-h-[5000px] space-y-[4px] opacity-100' : 'max-h-0 opacity-0'
913925
)}
914926
>
915927
{renderCollapsibleContent()}
@@ -1447,7 +1459,12 @@ function RunSkipButtons({
14471459
)
14481460
}
14491461

1450-
export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }: ToolCallProps) {
1462+
export function ToolCall({
1463+
toolCall: toolCallProp,
1464+
toolCallId,
1465+
onStateChange,
1466+
isCurrentMessage = true,
1467+
}: ToolCallProps) {
14511468
const [, forceUpdate] = useState({})
14521469
// Get live toolCall from store to ensure we have the latest state
14531470
const effectiveId = toolCallId || toolCallProp?.id
@@ -1536,6 +1553,7 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
15361553
<SubagentContentRenderer
15371554
toolCall={toolCall}
15381555
shouldCollapse={COLLAPSIBLE_SUBAGENTS.has(toolCall.name)}
1556+
isCurrentMessage={isCurrentMessage}
15391557
/>
15401558
)
15411559
}
@@ -1570,31 +1588,28 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
15701588
toolCall.name === 'make_api_request' ||
15711589
toolCall.name === 'set_global_workflow_variables'
15721590

1573-
const showButtons = shouldShowRunSkipButtons(toolCall)
1591+
const showButtons = isCurrentMessage && shouldShowRunSkipButtons(toolCall)
15741592

1575-
// Check UI config for secondary action
1593+
// Check UI config for secondary action - only show for current message tool calls
15761594
const toolUIConfig = getToolUIConfig(toolCall.name)
15771595
const secondaryAction = toolUIConfig?.secondaryAction
15781596
const showSecondaryAction = secondaryAction?.showInStates.includes(
15791597
toolCall.state as ClientToolCallState
15801598
)
1599+
const isExecuting =
1600+
toolCall.state === (ClientToolCallState.executing as any) ||
1601+
toolCall.state === ('executing' as any)
15811602

15821603
// Legacy fallbacks for tools that haven't migrated to UI config
15831604
const showMoveToBackground =
1584-
showSecondaryAction && secondaryAction?.text === 'Move to Background'
1585-
? true
1586-
: !secondaryAction &&
1587-
toolCall.name === 'run_workflow' &&
1588-
(toolCall.state === (ClientToolCallState.executing as any) ||
1589-
toolCall.state === ('executing' as any))
1605+
isCurrentMessage &&
1606+
((showSecondaryAction && secondaryAction?.text === 'Move to Background') ||
1607+
(!secondaryAction && toolCall.name === 'run_workflow' && isExecuting))
15901608

15911609
const showWake =
1592-
showSecondaryAction && secondaryAction?.text === 'Wake'
1593-
? true
1594-
: !secondaryAction &&
1595-
toolCall.name === 'sleep' &&
1596-
(toolCall.state === (ClientToolCallState.executing as any) ||
1597-
toolCall.state === ('executing' as any))
1610+
isCurrentMessage &&
1611+
((showSecondaryAction && secondaryAction?.text === 'Wake') ||
1612+
(!secondaryAction && toolCall.name === 'sleep' && isExecuting))
15981613

15991614
const handleStateChange = (state: any) => {
16001615
forceUpdate({})
@@ -1608,6 +1623,8 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
16081623
toolCall.state === ClientToolCallState.pending ||
16091624
toolCall.state === ClientToolCallState.executing
16101625

1626+
const shouldShowShimmer = isCurrentMessage && isLoadingState
1627+
16111628
const isSpecial = isSpecialToolCall(toolCall)
16121629

16131630
const renderPendingDetails = () => {
@@ -2019,7 +2036,7 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
20192036
<div className={isEnvVarsClickable ? 'cursor-pointer' : ''} onClick={handleEnvVarsClick}>
20202037
<ShimmerOverlayText
20212038
text={displayName}
2022-
active={isLoadingState}
2039+
active={shouldShowShimmer}
20232040
isSpecial={isSpecial}
20242041
className='font-[470] font-season text-[var(--text-secondary)] text-sm dark:text-[var(--text-muted)]'
20252042
/>
@@ -2051,7 +2068,7 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
20512068
{toolCall.subAgentBlocks && toolCall.subAgentBlocks.length > 0 && (
20522069
<SubAgentThinkingContent
20532070
blocks={toolCall.subAgentBlocks}
2054-
isStreaming={toolCall.subAgentStreaming}
2071+
isStreaming={isCurrentMessage && toolCall.subAgentStreaming}
20552072
/>
20562073
)}
20572074
</div>
@@ -2076,7 +2093,7 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
20762093
>
20772094
<ShimmerOverlayText
20782095
text={displayName}
2079-
active={isLoadingState}
2096+
active={shouldShowShimmer}
20802097
isSpecial={isSpecial}
20812098
className='font-[470] font-season text-[var(--text-secondary)] text-sm dark:text-[var(--text-muted)]'
20822099
/>
@@ -2112,7 +2129,7 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
21122129
{toolCall.subAgentBlocks && toolCall.subAgentBlocks.length > 0 && (
21132130
<SubAgentThinkingContent
21142131
blocks={toolCall.subAgentBlocks}
2115-
isStreaming={toolCall.subAgentStreaming}
2132+
isStreaming={isCurrentMessage && toolCall.subAgentStreaming}
21162133
/>
21172134
)}
21182135
</div>
@@ -2140,7 +2157,7 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
21402157
<div className={isToolNameClickable ? 'cursor-pointer' : ''} onClick={handleToolNameClick}>
21412158
<ShimmerOverlayText
21422159
text={displayName}
2143-
active={isLoadingState}
2160+
active={shouldShowShimmer}
21442161
isSpecial={isSpecial}
21452162
className='font-[470] font-season text-[var(--text-secondary)] text-sm dark:text-[var(--text-muted)]'
21462163
/>
@@ -2223,7 +2240,7 @@ export function ToolCall({ toolCall: toolCallProp, toolCallId, onStateChange }:
22232240
{toolCall.subAgentBlocks && toolCall.subAgentBlocks.length > 0 && (
22242241
<SubAgentThinkingContent
22252242
blocks={toolCall.subAgentBlocks}
2226-
isStreaming={toolCall.subAgentStreaming}
2243+
isStreaming={isCurrentMessage && toolCall.subAgentStreaming}
22272244
/>
22282245
)}
22292246
</div>

0 commit comments

Comments
 (0)