feat: improve message body rendering with enhanced expansion handling and animation triggers
This commit is contained in:
@@ -44,7 +44,6 @@ const useMigrationTimer = (
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!turnGroupingContext) return;
|
if (!turnGroupingContext) return;
|
||||||
if (!turnGroupingContext.isWorking) return;
|
if (!turnGroupingContext.isWorking) return;
|
||||||
if (turnGroupingContext.isGroupExpanded) return;
|
|
||||||
if (!hasPreviewableParts) return;
|
if (!hasPreviewableParts) return;
|
||||||
if (timerStartedRef.current) return;
|
if (timerStartedRef.current) return;
|
||||||
|
|
||||||
@@ -71,7 +70,7 @@ const useMigrationTimer = (
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!turnGroupingContext) return;
|
if (!turnGroupingContext) return;
|
||||||
if (!turnGroupingContext.isWorking || turnGroupingContext.isGroupExpanded || !hasPreviewableParts) {
|
if (!turnGroupingContext.isWorking || !hasPreviewableParts) {
|
||||||
if (timerRef.current) {
|
if (timerRef.current) {
|
||||||
window.clearTimeout(timerRef.current);
|
window.clearTimeout(timerRef.current);
|
||||||
timerRef.current = null;
|
timerRef.current = null;
|
||||||
@@ -611,7 +610,7 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
|||||||
const previewableActivityPartsForMessage = React.useMemo(() => {
|
const previewableActivityPartsForMessage = React.useMemo(() => {
|
||||||
if (!turnGroupingContext) return [];
|
if (!turnGroupingContext) return [];
|
||||||
if (!shouldShowActivityGroup) return [];
|
if (!shouldShowActivityGroup) return [];
|
||||||
if (!turnGroupingContext.isWorking || turnGroupingContext.isGroupExpanded) {
|
if (!turnGroupingContext.isWorking) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,30 +99,38 @@ const ProgressiveGroup: React.FC<ProgressiveGroupProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const previousExpandedRef = React.useRef<boolean | undefined>(isExpanded);
|
const previousExpandedRef = React.useRef<boolean | undefined>(isExpanded);
|
||||||
|
|
||||||
|
// Track expansion count to force re-mount of items when group expands from collapsed
|
||||||
|
const [expansionKey, setExpansionKey] = React.useState(0);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (previousExpandedRef.current === isExpanded) return;
|
if (previousExpandedRef.current === isExpanded) return;
|
||||||
|
const wasCollapsed = previousExpandedRef.current === false;
|
||||||
previousExpandedRef.current = isExpanded;
|
previousExpandedRef.current = isExpanded;
|
||||||
onContentChange?.('structural');
|
onContentChange?.('structural');
|
||||||
|
|
||||||
|
// Increment key when expanding to trigger fresh animations
|
||||||
|
if (isExpanded && wasCollapsed) {
|
||||||
|
setExpansionKey((k) => k + 1);
|
||||||
|
}
|
||||||
}, [isExpanded, onContentChange]);
|
}, [isExpanded, onContentChange]);
|
||||||
|
|
||||||
|
|
||||||
const displayParts = React.useMemo(() => {
|
const displayParts = React.useMemo(() => {
|
||||||
if (!isWorking) {
|
if (!isWorking) {
|
||||||
|
|
||||||
return sortPartsByTime(parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isExpanded) {
|
|
||||||
|
|
||||||
return sortPartsByTime(parts);
|
return sortPartsByTime(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// While turn is working, only show parts that have been "previewed".
|
||||||
|
// Collapsed mode previews them in-chat first, then migrates into Activity.
|
||||||
|
// Summary/Detailed modes skip in-chat preview, but still use the same migration gate.
|
||||||
return sortPartsByTime(
|
return sortPartsByTime(
|
||||||
parts.filter((activity) => {
|
parts.filter((activity) => {
|
||||||
const partId = activity.part.id;
|
const partId = activity.part.id;
|
||||||
return partId && previewedPartIds.has(activity.id);
|
return partId && previewedPartIds.has(activity.id);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, [parts, isWorking, isExpanded, previewedPartIds]);
|
}, [parts, isWorking, previewedPartIds]);
|
||||||
|
|
||||||
|
|
||||||
const summary = getGroupSummary(displayParts);
|
const summary = getGroupSummary(displayParts);
|
||||||
const toolConnections = getToolConnections(displayParts);
|
const toolConnections = getToolConnections(displayParts);
|
||||||
@@ -206,10 +214,12 @@ const ProgressiveGroup: React.FC<ProgressiveGroupProps> = ({
|
|||||||
const partId = activity.part.id || `group-part-${index}`;
|
const partId = activity.part.id || `group-part-${index}`;
|
||||||
const connection = toolConnections[partId];
|
const connection = toolConnections[partId];
|
||||||
|
|
||||||
|
const animationKey = `${partId}-exp${expansionKey}`;
|
||||||
|
|
||||||
switch (activity.kind) {
|
switch (activity.kind) {
|
||||||
case 'tool':
|
case 'tool':
|
||||||
return (
|
return (
|
||||||
<FadeInOnReveal key={partId}>
|
<FadeInOnReveal key={animationKey}>
|
||||||
<ToolPart
|
<ToolPart
|
||||||
part={activity.part as ToolPartType}
|
part={activity.part as ToolPartType}
|
||||||
isExpanded={expandedTools.has(partId)}
|
isExpanded={expandedTools.has(partId)}
|
||||||
@@ -225,7 +235,7 @@ const ProgressiveGroup: React.FC<ProgressiveGroupProps> = ({
|
|||||||
|
|
||||||
case 'reasoning':
|
case 'reasoning':
|
||||||
return (
|
return (
|
||||||
<FadeInOnReveal key={partId}>
|
<FadeInOnReveal key={animationKey}>
|
||||||
<ReasoningPart
|
<ReasoningPart
|
||||||
part={activity.part}
|
part={activity.part}
|
||||||
messageId={activity.messageId}
|
messageId={activity.messageId}
|
||||||
@@ -236,7 +246,7 @@ const ProgressiveGroup: React.FC<ProgressiveGroupProps> = ({
|
|||||||
|
|
||||||
case 'justification':
|
case 'justification':
|
||||||
return (
|
return (
|
||||||
<FadeInOnReveal key={partId}>
|
<FadeInOnReveal key={animationKey}>
|
||||||
<JustificationBlock
|
<JustificationBlock
|
||||||
part={activity.part}
|
part={activity.part}
|
||||||
messageId={activity.messageId}
|
messageId={activity.messageId}
|
||||||
|
|||||||
Reference in New Issue
Block a user