fix: drop stale chat memo boundaries
Remove remaining chat row and body memo comparators that were still keeping finished background session responses blank or stale in the UI.
This commit is contained in:
@@ -30,7 +30,6 @@ import type { TurnGroupingContext } from './lib/turns/types';
|
|||||||
import { copyTextToClipboard } from '@/lib/clipboard';
|
import { copyTextToClipboard } from '@/lib/clipboard';
|
||||||
import { FadeInOnReveal } from './message/FadeInOnReveal';
|
import { FadeInOnReveal } from './message/FadeInOnReveal';
|
||||||
import { streamPerfCount } from '@/stores/utils/streamDebug';
|
import { streamPerfCount } from '@/stores/utils/streamDebug';
|
||||||
import { areOptionalRenderRelevantMessagesEqual, areRelevantTurnGroupingContextsEqual, areRenderRelevantMessageInfoEqual, areRenderRelevantPartsEqual } from './message/renderCompare';
|
|
||||||
|
|
||||||
const ToolOutputDialog = React.lazy(() => import('./message/ToolOutputDialog'));
|
const ToolOutputDialog = React.lazy(() => import('./message/ToolOutputDialog'));
|
||||||
|
|
||||||
@@ -1128,18 +1127,4 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(ChatMessage, (prev, next) => {
|
export default ChatMessage;
|
||||||
const prevRole = deriveMessageRole(prev.message.info);
|
|
||||||
|
|
||||||
return areRenderRelevantMessageInfoEqual(prev.message.info, next.message.info)
|
|
||||||
&& areRenderRelevantPartsEqual(prev.message.parts, next.message.parts)
|
|
||||||
&& areOptionalRenderRelevantMessagesEqual(prev.previousMessage, next.previousMessage)
|
|
||||||
&& areOptionalRenderRelevantMessagesEqual(prev.nextMessage, next.nextMessage)
|
|
||||||
&& prev.onContentChange === next.onContentChange
|
|
||||||
&& areRelevantTurnGroupingContextsEqual(prev.turnGroupingContext, next.turnGroupingContext, prev.message.info.id, prevRole.isUser)
|
|
||||||
&& prev.assistantHeaderMessageId === next.assistantHeaderMessageId
|
|
||||||
&& prev.isInActiveTurn === next.isInActiveTurn
|
|
||||||
&& prev.activeStreamingPhase === next.activeStreamingPhase
|
|
||||||
&& prev.animateUserOnMount === next.animateUserOnMount
|
|
||||||
&& prev.onUserAnimationConsumed === next.onUserAnimationConsumed;
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -797,7 +797,7 @@ interface UngroupedMessageRowProps {
|
|||||||
activeStreamingPhase?: StreamPhase | null;
|
activeStreamingPhase?: StreamPhase | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UngroupedMessageRow: React.FC<UngroupedMessageRowProps> = React.memo(({
|
const UngroupedMessageRow: React.FC<UngroupedMessageRowProps> = ({
|
||||||
message,
|
message,
|
||||||
previousMessage,
|
previousMessage,
|
||||||
nextMessage,
|
nextMessage,
|
||||||
@@ -823,18 +823,7 @@ const UngroupedMessageRow: React.FC<UngroupedMessageRowProps> = React.memo(({
|
|||||||
activeStreamingPhase={message.info.id === activeStreamingMessageId ? activeStreamingPhase : null}
|
activeStreamingPhase={message.info.id === activeStreamingMessageId ? activeStreamingPhase : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, (prev, next) => {
|
};
|
||||||
return areRenderRelevantMessagesEqual(prev.message, next.message)
|
|
||||||
&& areOptionalRenderRelevantMessagesEqual(prev.previousMessage, next.previousMessage)
|
|
||||||
&& areOptionalRenderRelevantMessagesEqual(prev.nextMessage, next.nextMessage)
|
|
||||||
&& prev.onMessageContentChange === next.onMessageContentChange
|
|
||||||
&& prev.getAnimationHandlers === next.getAnimationHandlers
|
|
||||||
&& prev.scrollToBottom === next.scrollToBottom
|
|
||||||
&& prev.shouldAnimateUserMessage === next.shouldAnimateUserMessage
|
|
||||||
&& prev.onUserAnimationConsumed === next.onUserAnimationConsumed
|
|
||||||
&& prev.activeStreamingMessageId === next.activeStreamingMessageId
|
|
||||||
&& prev.activeStreamingPhase === next.activeStreamingPhase;
|
|
||||||
});
|
|
||||||
|
|
||||||
UngroupedMessageRow.displayName = 'UngroupedMessageRow';
|
UngroupedMessageRow.displayName = 'UngroupedMessageRow';
|
||||||
|
|
||||||
@@ -867,7 +856,7 @@ const turnContainsMessageId = (turn: TurnRecord, messageId: string | null | unde
|
|||||||
return turn.assistantMessages.some((assistant) => assistant.info.id === messageId);
|
return turn.assistantMessages.some((assistant) => assistant.info.id === messageId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const MessageListEntry: React.FC<MessageListEntryProps> = React.memo(({
|
const MessageListEntry: React.FC<MessageListEntryProps> = ({
|
||||||
entry,
|
entry,
|
||||||
onMessageContentChange,
|
onMessageContentChange,
|
||||||
getAnimationHandlers,
|
getAnimationHandlers,
|
||||||
@@ -919,96 +908,10 @@ const MessageListEntry: React.FC<MessageListEntryProps> = React.memo(({
|
|||||||
stickyUserHeader={stickyUserHeader}
|
stickyUserHeader={stickyUserHeader}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, areMessageListEntryPropsEqual);
|
};
|
||||||
|
|
||||||
MessageListEntry.displayName = 'MessageListEntry';
|
MessageListEntry.displayName = 'MessageListEntry';
|
||||||
|
|
||||||
function areMessageListEntryPropsEqual(prevProps: MessageListEntryProps, nextProps: MessageListEntryProps): boolean {
|
|
||||||
if (prevProps.stickyUserHeader !== nextProps.stickyUserHeader) return false;
|
|
||||||
if (prevProps.chatRenderMode !== nextProps.chatRenderMode) return false;
|
|
||||||
if (prevProps.shouldAnimateUserMessage !== nextProps.shouldAnimateUserMessage) return false;
|
|
||||||
|
|
||||||
const prevEntry = prevProps.entry;
|
|
||||||
const nextEntry = nextProps.entry;
|
|
||||||
if (prevEntry.kind !== nextEntry.kind) return false;
|
|
||||||
if (prevEntry.key !== nextEntry.key) return false;
|
|
||||||
|
|
||||||
if (prevEntry.kind === 'turn' && nextEntry.kind === 'turn') {
|
|
||||||
if (prevEntry.turn !== nextEntry.turn || prevEntry.isLastTurn !== nextEntry.isLastTurn) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevExpanded = (prevProps.turnUiStates.get(prevEntry.turn.turnId) ?? { isExpanded: prevProps.defaultActivityExpanded }).isExpanded;
|
|
||||||
const nextExpanded = (nextProps.turnUiStates.get(nextEntry.turn.turnId) ?? { isExpanded: nextProps.defaultActivityExpanded }).isExpanded;
|
|
||||||
if (prevExpanded !== nextExpanded) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevEntry.isLastTurn && prevProps.sessionIsWorking !== nextProps.sessionIsWorking) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevProps.activeStreamingMessageId !== nextProps.activeStreamingMessageId) {
|
|
||||||
const prevAffected = turnContainsMessageId(prevEntry.turn, prevProps.activeStreamingMessageId);
|
|
||||||
const nextAffected = turnContainsMessageId(nextEntry.turn, nextProps.activeStreamingMessageId);
|
|
||||||
if (prevAffected || nextAffected) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevProps.activeStreamingPhase !== nextProps.activeStreamingPhase) {
|
|
||||||
const prevAffected = turnContainsMessageId(prevEntry.turn, prevProps.activeStreamingMessageId);
|
|
||||||
const nextAffected = turnContainsMessageId(nextEntry.turn, nextProps.activeStreamingMessageId);
|
|
||||||
if (prevAffected || nextAffected) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevProps.activeStreamingPhase !== nextProps.activeStreamingPhase) {
|
|
||||||
const prevAffected = turnContainsMessageId(prevEntry.turn, prevProps.activeStreamingMessageId);
|
|
||||||
const nextAffected = turnContainsMessageId(nextEntry.turn, nextProps.activeStreamingMessageId);
|
|
||||||
if (prevAffected || nextAffected) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevEntry.kind === 'ungrouped' && nextEntry.kind === 'ungrouped') {
|
|
||||||
if (prevProps.activeStreamingMessageId !== nextProps.activeStreamingMessageId) {
|
|
||||||
const messageId = prevEntry.message.info.id;
|
|
||||||
const prevActive = prevProps.activeStreamingMessageId === messageId;
|
|
||||||
const nextActive = nextProps.activeStreamingMessageId === messageId;
|
|
||||||
if (prevActive !== nextActive) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevProps.activeStreamingPhase !== nextProps.activeStreamingPhase) {
|
|
||||||
const messageId = prevEntry.message.info.id;
|
|
||||||
if (prevProps.activeStreamingMessageId === messageId || nextProps.activeStreamingMessageId === messageId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevProps.activeStreamingPhase !== nextProps.activeStreamingPhase) {
|
|
||||||
const messageId = prevEntry.message.info.id;
|
|
||||||
if (prevProps.activeStreamingMessageId === messageId || nextProps.activeStreamingMessageId === messageId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
areRenderRelevantMessagesEqual(prevEntry.message, nextEntry.message)
|
|
||||||
&& areOptionalRenderRelevantMessagesEqual(prevEntry.previousMessage, nextEntry.previousMessage)
|
|
||||||
&& areOptionalRenderRelevantMessagesEqual(prevEntry.nextMessage, nextEntry.nextMessage)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inner component that renders staged turn entries.
|
// Inner component that renders staged turn entries.
|
||||||
const StaticHistoryList: React.FC<{
|
const StaticHistoryList: React.FC<{
|
||||||
entries: RenderEntry[];
|
entries: RenderEntry[];
|
||||||
@@ -1028,7 +931,7 @@ const StaticHistoryList: React.FC<{
|
|||||||
shouldAnimateUserMessage: (message: ChatMessageEntry) => boolean;
|
shouldAnimateUserMessage: (message: ChatMessageEntry) => boolean;
|
||||||
onUserAnimationConsumed: (messageId: string) => void;
|
onUserAnimationConsumed: (messageId: string) => void;
|
||||||
activeStreamingPhase?: StreamPhase | null;
|
activeStreamingPhase?: StreamPhase | null;
|
||||||
}> = React.memo(({ entries, shouldVirtualize, virtualRows, totalSize, measureElement, contentRef, onMessageContentChange, getAnimationHandlers, scrollToBottom, stickyUserHeader, defaultActivityExpanded, turnUiStates, onToggleTurnGroup, chatRenderMode, shouldAnimateUserMessage, onUserAnimationConsumed, activeStreamingPhase }) => {
|
}> = ({ entries, shouldVirtualize, virtualRows, totalSize, measureElement, contentRef, onMessageContentChange, getAnimationHandlers, scrollToBottom, stickyUserHeader, defaultActivityExpanded, turnUiStates, onToggleTurnGroup, chatRenderMode, shouldAnimateUserMessage, onUserAnimationConsumed, activeStreamingPhase }) => {
|
||||||
const renderEntry = React.useCallback((entry: RenderEntry) => {
|
const renderEntry = React.useCallback((entry: RenderEntry) => {
|
||||||
return (
|
return (
|
||||||
<MessageListEntry
|
<MessageListEntry
|
||||||
@@ -1096,25 +999,7 @@ const StaticHistoryList: React.FC<{
|
|||||||
{paddingBottom > 0 ? <div aria-hidden="true" style={{ height: `${paddingBottom}px` }} /> : null}
|
{paddingBottom > 0 ? <div aria-hidden="true" style={{ height: `${paddingBottom}px` }} /> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, (prevProps, nextProps) => {
|
};
|
||||||
return prevProps.entries === nextProps.entries
|
|
||||||
&& prevProps.shouldVirtualize === nextProps.shouldVirtualize
|
|
||||||
&& prevProps.virtualRows === nextProps.virtualRows
|
|
||||||
&& prevProps.totalSize === nextProps.totalSize
|
|
||||||
&& prevProps.measureElement === nextProps.measureElement
|
|
||||||
&& prevProps.contentRef === nextProps.contentRef
|
|
||||||
&& prevProps.onMessageContentChange === nextProps.onMessageContentChange
|
|
||||||
&& prevProps.getAnimationHandlers === nextProps.getAnimationHandlers
|
|
||||||
&& prevProps.scrollToBottom === nextProps.scrollToBottom
|
|
||||||
&& prevProps.stickyUserHeader === nextProps.stickyUserHeader
|
|
||||||
&& prevProps.defaultActivityExpanded === nextProps.defaultActivityExpanded
|
|
||||||
&& prevProps.turnUiStates === nextProps.turnUiStates
|
|
||||||
&& prevProps.onToggleTurnGroup === nextProps.onToggleTurnGroup
|
|
||||||
&& prevProps.chatRenderMode === nextProps.chatRenderMode
|
|
||||||
&& prevProps.shouldAnimateUserMessage === nextProps.shouldAnimateUserMessage
|
|
||||||
&& prevProps.onUserAnimationConsumed === nextProps.onUserAnimationConsumed
|
|
||||||
&& prevProps.activeStreamingPhase === nextProps.activeStreamingPhase;
|
|
||||||
});
|
|
||||||
|
|
||||||
StaticHistoryList.displayName = 'StaticHistoryList';
|
StaticHistoryList.displayName = 'StaticHistoryList';
|
||||||
|
|
||||||
@@ -1133,7 +1018,7 @@ const StreamingTailContent: React.FC<{
|
|||||||
onUserAnimationConsumed: (messageId: string) => void;
|
onUserAnimationConsumed: (messageId: string) => void;
|
||||||
activeStreamingMessageId?: string | null;
|
activeStreamingMessageId?: string | null;
|
||||||
activeStreamingPhase?: StreamPhase | null;
|
activeStreamingPhase?: StreamPhase | null;
|
||||||
}> = React.memo(({
|
}> = ({
|
||||||
entry,
|
entry,
|
||||||
onMessageContentChange,
|
onMessageContentChange,
|
||||||
getAnimationHandlers,
|
getAnimationHandlers,
|
||||||
@@ -1167,22 +1052,7 @@ const StreamingTailContent: React.FC<{
|
|||||||
activeStreamingPhase={activeStreamingPhase}
|
activeStreamingPhase={activeStreamingPhase}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, (prev, next) => {
|
};
|
||||||
return prev.entry === next.entry
|
|
||||||
&& prev.onMessageContentChange === next.onMessageContentChange
|
|
||||||
&& prev.getAnimationHandlers === next.getAnimationHandlers
|
|
||||||
&& prev.scrollToBottom === next.scrollToBottom
|
|
||||||
&& prev.stickyUserHeader === next.stickyUserHeader
|
|
||||||
&& prev.sessionIsWorking === next.sessionIsWorking
|
|
||||||
&& prev.defaultActivityExpanded === next.defaultActivityExpanded
|
|
||||||
&& prev.turnUiStates === next.turnUiStates
|
|
||||||
&& prev.onToggleTurnGroup === next.onToggleTurnGroup
|
|
||||||
&& prev.chatRenderMode === next.chatRenderMode
|
|
||||||
&& prev.shouldAnimateUserMessage === next.shouldAnimateUserMessage
|
|
||||||
&& prev.onUserAnimationConsumed === next.onUserAnimationConsumed
|
|
||||||
&& prev.activeStreamingMessageId === next.activeStreamingMessageId
|
|
||||||
&& prev.activeStreamingPhase === next.activeStreamingPhase;
|
|
||||||
});
|
|
||||||
|
|
||||||
StreamingTailContent.displayName = 'StreamingTailContent';
|
StreamingTailContent.displayName = 'StreamingTailContent';
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import { ToolRevealOnMount } from './parts/ToolRevealOnMount';
|
|||||||
import { StaticToolRow } from './parts/ProgressiveGroup';
|
import { StaticToolRow } from './parts/ProgressiveGroup';
|
||||||
import { isExpandableTool, isStandaloneTool } from './parts/toolRenderUtils';
|
import { isExpandableTool, isStandaloneTool } from './parts/toolRenderUtils';
|
||||||
import TurnActivity from '../components/TurnActivity';
|
import TurnActivity from '../components/TurnActivity';
|
||||||
import { areRenderRelevantPartsEqual } from './renderCompare';
|
|
||||||
|
|
||||||
type SubtaskPartLike = Part & {
|
type SubtaskPartLike = Part & {
|
||||||
type: 'subtask';
|
type: 'subtask';
|
||||||
@@ -1549,37 +1548,4 @@ const MessageBody: React.FC<MessageBodyProps> = ({ isUser, ...props }) => {
|
|||||||
return <AssistantMessageBody {...props} />;
|
return <AssistantMessageBody {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(MessageBody, (prev, next) => {
|
export default MessageBody;
|
||||||
return prev.sessionId === next.sessionId
|
|
||||||
&& prev.messageId === next.messageId
|
|
||||||
&& prev.isUser === next.isUser
|
|
||||||
&& areRenderRelevantPartsEqual(prev.parts, next.parts)
|
|
||||||
&& prev.isMessageCompleted === next.isMessageCompleted
|
|
||||||
&& prev.messageFinish === next.messageFinish
|
|
||||||
&& prev.messageCompletedAt === next.messageCompletedAt
|
|
||||||
&& prev.messageCreatedAt === next.messageCreatedAt
|
|
||||||
&& prev.syntaxTheme === next.syntaxTheme
|
|
||||||
&& prev.isMobile === next.isMobile
|
|
||||||
&& prev.hasTouchInput === next.hasTouchInput
|
|
||||||
&& prev.copiedCode === next.copiedCode
|
|
||||||
&& prev.expandedTools === next.expandedTools
|
|
||||||
&& prev.streamPhase === next.streamPhase
|
|
||||||
&& prev.allowAnimation === next.allowAnimation
|
|
||||||
&& prev.shouldShowHeader === next.shouldShowHeader
|
|
||||||
&& prev.hasTextContent === next.hasTextContent
|
|
||||||
&& prev.copiedMessage === next.copiedMessage
|
|
||||||
&& prev.showReasoningTraces === next.showReasoningTraces
|
|
||||||
&& prev.agentMention === next.agentMention
|
|
||||||
&& prev.turnGroupingContext === next.turnGroupingContext
|
|
||||||
&& prev.errorMessage === next.errorMessage
|
|
||||||
&& prev.userActionsMode === next.userActionsMode
|
|
||||||
&& prev.stickyUserHeaderEnabled === next.stickyUserHeaderEnabled
|
|
||||||
&& prev.onCopyCode === next.onCopyCode
|
|
||||||
&& prev.onToggleTool === next.onToggleTool
|
|
||||||
&& prev.onShowPopup === next.onShowPopup
|
|
||||||
&& prev.onContentChange === next.onContentChange
|
|
||||||
&& prev.onCopyMessage === next.onCopyMessage
|
|
||||||
&& prev.onAuxiliaryContentComplete === next.onAuxiliaryContentComplete
|
|
||||||
&& prev.onRevert === next.onRevert
|
|
||||||
&& prev.onFork === next.onFork;
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user