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
This commit is contained in:
Bohdan Triapitsyn
2026-05-17 23:55:22 +03:00
parent 5c1333a139
commit 9cf0fe0551
@@ -187,26 +187,28 @@ const LiveDuration: React.FC<{ start: number; end?: number; active: boolean }> =
return <>{formatDuration(start, end, now)}</>; return <>{formatDuration(start, end, now)}</>;
}; };
const useDeferredExpandedContent = (isExpanded: boolean) => { const EXPANDED_CONTENT_TRANSITION_MS = 350;
const [shouldRender, setShouldRender] = React.useState(false);
const useAnimatedExpandedContent = (isExpanded: boolean) => {
const [shouldRender, setShouldRender] = React.useState(isExpanded);
React.useEffect(() => { React.useEffect(() => {
if (!isExpanded) {
setShouldRender(false);
return;
}
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
setShouldRender(isExpanded);
return;
}
if (isExpanded) {
setShouldRender(true); setShouldRender(true);
return; return;
} }
const frame = window.requestAnimationFrame(() => { const timer = window.setTimeout(() => {
setShouldRender(true); setShouldRender(false);
}); }, EXPANDED_CONTENT_TRANSITION_MS);
return () => { return () => {
window.cancelAnimationFrame(frame); window.clearTimeout(timer);
}; };
}, [isExpanded]); }, [isExpanded]);
@@ -2528,7 +2530,7 @@ const ToolPart: React.FC<ToolPartProps> = ({
const iconStyle = !isTaskTool && isError ? TOOL_ERROR_ICON_STYLE : TOOL_NORMAL_ICON_STYLE; 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 titleStyle = !isTaskTool && isError ? TOOL_ERROR_TITLE_STYLE : TOOL_NORMAL_TITLE_STYLE;
const shouldRenderExpandedContent = useDeferredExpandedContent(isExpanded); const shouldRenderExpandedContent = useAnimatedExpandedContent(isExpanded);
if (!shouldTreatAsFinalized && !isActive && !isTaskTool) { if (!shouldTreatAsFinalized && !isActive && !isTaskTool) {
return null; return null;
@@ -2672,7 +2674,16 @@ const ToolPart: React.FC<ToolPartProps> = ({
/> />
) : null} ) : null}
{!isTaskTool && shouldRenderExpandedContent ? ( {!isTaskTool ? (
<div
aria-hidden={!isExpanded}
className={cn(
'grid transition-[grid-template-rows,opacity] duration-[350ms] ease-out motion-reduce:transition-none',
isExpanded ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0',
)}
>
<div className="min-h-0 overflow-hidden">
{shouldRenderExpandedContent ? (
<div className="relative ml-2 pl-3"> <div className="relative ml-2 pl-3">
<span <span
aria-hidden="true" aria-hidden="true"
@@ -2689,6 +2700,9 @@ const ToolPart: React.FC<ToolPartProps> = ({
</div> </div>
) : null} ) : null}
</div> </div>
</div>
) : null}
</div>
); );
}; };