From 9cf0fe05515a45fdf6c354b518166efcfe14f27c Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Sun, 17 May 2026 23:55:22 +0300 Subject: [PATCH] feat: animate expandable tool details Add smooth expand and collapse transitions to tool details Match the existing thinking block animation style Keep existing default expanded states unchanged --- .../chat/message/parts/ToolPart.tsx | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/packages/ui/src/components/chat/message/parts/ToolPart.tsx b/packages/ui/src/components/chat/message/parts/ToolPart.tsx index 77274067..ba89a771 100644 --- a/packages/ui/src/components/chat/message/parts/ToolPart.tsx +++ b/packages/ui/src/components/chat/message/parts/ToolPart.tsx @@ -187,26 +187,28 @@ const LiveDuration: React.FC<{ start: number; end?: number; active: boolean }> = return <>{formatDuration(start, end, now)}; }; -const useDeferredExpandedContent = (isExpanded: boolean) => { - const [shouldRender, setShouldRender] = React.useState(false); +const EXPANDED_CONTENT_TRANSITION_MS = 350; + +const useAnimatedExpandedContent = (isExpanded: boolean) => { + const [shouldRender, setShouldRender] = React.useState(isExpanded); React.useEffect(() => { - if (!isExpanded) { - setShouldRender(false); - return; - } - if (typeof window === 'undefined') { + setShouldRender(isExpanded); + return; + } + + if (isExpanded) { setShouldRender(true); return; } - const frame = window.requestAnimationFrame(() => { - setShouldRender(true); - }); + const timer = window.setTimeout(() => { + setShouldRender(false); + }, EXPANDED_CONTENT_TRANSITION_MS); return () => { - window.cancelAnimationFrame(frame); + window.clearTimeout(timer); }; }, [isExpanded]); @@ -2528,7 +2530,7 @@ const ToolPart: React.FC = ({ const iconStyle = !isTaskTool && isError ? TOOL_ERROR_ICON_STYLE : TOOL_NORMAL_ICON_STYLE; const titleStyle = !isTaskTool && isError ? TOOL_ERROR_TITLE_STYLE : TOOL_NORMAL_TITLE_STYLE; - const shouldRenderExpandedContent = useDeferredExpandedContent(isExpanded); + const shouldRenderExpandedContent = useAnimatedExpandedContent(isExpanded); if (!shouldTreatAsFinalized && !isActive && !isTaskTool) { return null; @@ -2672,20 +2674,32 @@ const ToolPart: React.FC = ({ /> ) : null} - {!isTaskTool && shouldRenderExpandedContent ? ( -
-