fix: animate tool paths in sorted chat mode

Adds animated reveals for file path tool descriptions
Keeps sorted mode tool animations working without extra chat gaps
Preserves file icons, truncation, and click behavior
This commit is contained in:
Bohdan Triapitsyn
2026-05-15 22:58:46 +03:00
parent 774fb4be66
commit 88fc8bff22
3 changed files with 29 additions and 12 deletions
@@ -700,6 +700,7 @@ const TurnBlock = React.memo(({
const isFirstAssistant = assistantIndex === 0; const isFirstAssistant = assistantIndex === 0;
const isLastAssistant = assistantIndex === visibleAssistantMessages.length - 1; const isLastAssistant = assistantIndex === visibleAssistantMessages.length - 1;
const isActivityOwner = Boolean(activityOwnerMessageId) && message.info.id === activityOwnerMessageId; const isActivityOwner = Boolean(activityOwnerMessageId) && message.info.id === activityOwnerMessageId;
const hasAnchoredActivitySegment = visibleActivitySegments.some((segment) => segment.anchorMessageId === message.info.id);
const shouldAttachFullTurnContext = chatRenderMode === 'sorted' const shouldAttachFullTurnContext = chatRenderMode === 'sorted'
? isAssistantMessage ? isAssistantMessage
: (isActivityOwner || isFirstAssistant || isLastAssistant); : (isActivityOwner || isFirstAssistant || isLastAssistant);
@@ -722,7 +723,11 @@ const TurnBlock = React.memo(({
activityOwnerMessageId, activityOwnerMessageId,
isFirstAssistantInTurn: isFirstAssistant, isFirstAssistantInTurn: isFirstAssistant,
isLastAssistantInTurn: isLastAssistant, isLastAssistantInTurn: isLastAssistant,
isWorking: isLastTurn && sessionIsWorking && message.info.id === streamingAssistantMessageId, isWorking: isLastTurn && sessionIsWorking && (
chatRenderMode === 'sorted'
? hasAnchoredActivitySegment
: message.info.id === streamingAssistantMessageId
),
hasTools: turn.hasTools, hasTools: turn.hasTools,
hasReasoning: turn.hasReasoning, hasReasoning: turn.hasReasoning,
...(shouldAttachFullTurnContext ? { ...(shouldAttachFullTurnContext ? {
@@ -777,6 +782,7 @@ const TurnBlock = React.memo(({
activeStreamingPhase, activeStreamingPhase,
visibleAssistantMessages, visibleAssistantMessages,
visibleAssistantIds, visibleAssistantIds,
visibleActivitySegments,
activityOwnerMessageId, activityOwnerMessageId,
shouldAnimateUserMessage, shouldAnimateUserMessage,
onUserAnimationConsumed, onUserAnimationConsumed,
@@ -242,18 +242,19 @@ const getRelativePathFromDirectory = (filePath: string, currentDirectory: string
return normalizedPath; return normalizedPath;
}; };
const renderReadFilePath = (displayPath: string) => { const renderReadFilePath = (displayPath: string, animate = true) => {
const lastSlash = displayPath.lastIndexOf('/'); const lastSlash = displayPath.lastIndexOf('/');
if (lastSlash === -1) { if (lastSlash === -1) {
return ( return (
<span <Text
variant={animate ? 'generate-effect' : 'static'}
className="min-w-0 flex-1 truncate whitespace-nowrap typography-meta leading-5" className="min-w-0 flex-1 truncate whitespace-nowrap typography-meta leading-5"
style={{ color: 'var(--tools-title)' }} style={{ color: 'var(--tools-title)' }}
title={displayPath} title={displayPath}
> >
{displayPath} {displayPath}
</span> </Text>
); );
} }
@@ -277,7 +278,13 @@ const renderReadFilePath = (displayPath: string) => {
{displayDir} {displayDir}
</span> </span>
<span className="flex-shrink-0" style={{ color: 'var(--tools-description)' }}>/</span> <span className="flex-shrink-0" style={{ color: 'var(--tools-description)' }}>/</span>
<span className="flex-shrink-0" style={{ color: 'var(--tools-title)' }}>{name}</span> <Text
variant={animate ? 'generate-effect' : 'static'}
className="flex-shrink-0"
style={{ color: 'var(--tools-title)' }}
>
{name}
</Text>
</span> </span>
); );
}; };
@@ -682,7 +689,7 @@ const StaticToolRowInner: React.FC<{
title={entry.offset ? `${entry.displayPath}:${entry.offset}` : entry.displayPath} title={entry.offset ? `${entry.displayPath}:${entry.offset}` : entry.displayPath}
> >
{showToolFileIcons ? <FileTypeIcon filePath={entry.path} className="h-3.5 w-3.5" /> : null} {showToolFileIcons ? <FileTypeIcon filePath={entry.path} className="h-3.5 w-3.5" /> : null}
{renderReadFilePath(entry.displayPath)} {renderReadFilePath(entry.displayPath, animateTailText)}
</button> </button>
)) ))
: null} : null}
@@ -1359,20 +1359,20 @@ const renderPathLikeGitChanges = (path: string, grow = true) => {
); );
}; };
const renderAnimatedPathWithIcon = (path: string, _animate = true, grow = true, showFileIcons = true) => { const renderAnimatedPathWithIcon = (path: string, animate = true, grow = true, showFileIcons = true) => {
void _animate;
const lastSlash = path.lastIndexOf('/'); const lastSlash = path.lastIndexOf('/');
if (lastSlash === -1) { if (lastSlash === -1) {
return ( return (
<span className={cn('min-w-0 inline-flex items-center gap-1 overflow-hidden', grow && 'flex-1')} title={path}> <span className={cn('min-w-0 inline-flex items-center gap-1 overflow-hidden', grow && 'flex-1')} title={path}>
{showFileIcons ? <FileTypeIcon filePath={path} className="h-3.5 w-3.5 flex-shrink-0" /> : null} {showFileIcons ? <FileTypeIcon filePath={path} className="h-3.5 w-3.5 flex-shrink-0" /> : null}
<span <Text
variant={animate ? 'generate-effect' : 'static'}
className={cn('min-w-0 truncate whitespace-nowrap typography-meta', grow && 'flex-1')} className={cn('min-w-0 truncate whitespace-nowrap typography-meta', grow && 'flex-1')}
style={{ color: 'var(--tools-title)' }} style={{ color: 'var(--tools-title)' }}
> >
{path} {path}
</span> </Text>
</span> </span>
); );
} }
@@ -1399,9 +1399,13 @@ const renderAnimatedPathWithIcon = (path: string, _animate = true, grow = true,
{displayDir} {displayDir}
</span> </span>
<span className="flex-shrink-0" style={{ color: 'var(--tools-description)' }}>/</span> <span className="flex-shrink-0" style={{ color: 'var(--tools-description)' }}>/</span>
<span className="flex-shrink-0" style={{ color: 'var(--tools-title)' }}> <Text
variant={animate ? 'generate-effect' : 'static'}
className="flex-shrink-0"
style={{ color: 'var(--tools-title)' }}
>
{name} {name}
</span> </Text>
</span> </span>
</span> </span>
); );