perf: avoid per-message review metadata checks

Compute review transfer state once per chat render
Hide transfer actions when linked review sessions are inactive
Remove session-list scans from individual message rows
This commit is contained in:
Bohdan Triapitsyn
2026-06-07 01:47:59 +03:00
parent 2e77376ca0
commit 3db5a3cc0b
4 changed files with 57 additions and 21 deletions
@@ -31,6 +31,7 @@ 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, areRenderRelevantMessagesEqual, areRelevantTurnGroupingContextsEqual } from './message/renderCompare'; import { areOptionalRenderRelevantMessagesEqual, areRenderRelevantMessagesEqual, areRelevantTurnGroupingContextsEqual } from './message/renderCompare';
import type { ReviewTransferDirection } from '@/lib/reviewFlow';
const ToolOutputDialog = lazyWithChunkRecovery(() => import('./message/ToolOutputDialog')); const ToolOutputDialog = lazyWithChunkRecovery(() => import('./message/ToolOutputDialog'));
@@ -133,6 +134,7 @@ interface ChatMessageProps {
activeStreamingPhase?: StreamPhase | null; activeStreamingPhase?: StreamPhase | null;
animateUserOnMount?: boolean; animateUserOnMount?: boolean;
onUserAnimationConsumed?: (messageId: string) => void; onUserAnimationConsumed?: (messageId: string) => void;
reviewTransferDirection?: ReviewTransferDirection | null;
} }
const ChatMessage: React.FC<ChatMessageProps> = ({ const ChatMessage: React.FC<ChatMessageProps> = ({
@@ -147,6 +149,7 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
activeStreamingPhase = null, activeStreamingPhase = null,
animateUserOnMount = false, animateUserOnMount = false,
onUserAnimationConsumed, onUserAnimationConsumed,
reviewTransferDirection = null,
}) => { }) => {
const { isMobile, isTablet, hasTouchInput } = useDeviceInfo(); const { isMobile, isTablet, hasTouchInput } = useDeviceInfo();
const alwaysShowMessageActions = isMobile || isTablet; const alwaysShowMessageActions = isMobile || isTablet;
@@ -1138,6 +1141,7 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
turnGroupingContext={turnGroupingContext} turnGroupingContext={turnGroupingContext}
errorMessage={assistantErrorText} errorMessage={assistantErrorText}
errorVariant={assistantErrorVariant} errorVariant={assistantErrorVariant}
reviewTransferDirection={reviewTransferDirection}
/> />
</div> </div>
@@ -1171,6 +1175,7 @@ export default React.memo(ChatMessage, (prev, next) => {
) )
&& prev.isInActiveTurn === next.isInActiveTurn && prev.isInActiveTurn === next.isInActiveTurn
&& prev.activeStreamingPhase === next.activeStreamingPhase && prev.activeStreamingPhase === next.activeStreamingPhase
&& prev.reviewTransferDirection === next.reviewTransferDirection
&& prev.assistantHeaderMessageId === next.assistantHeaderMessageId && prev.assistantHeaderMessageId === next.assistantHeaderMessageId
&& prev.animateUserOnMount === next.animateUserOnMount && prev.animateUserOnMount === next.animateUserOnMount
&& prev.onUserAnimationConsumed === next.onUserAnimationConsumed && prev.onUserAnimationConsumed === next.onUserAnimationConsumed
@@ -16,6 +16,9 @@ import { hasPendingUserSendAnimation, consumePendingUserSendAnimation } from '@/
import { streamPerfCount, streamPerfMeasure } from '@/stores/utils/streamDebug'; import { streamPerfCount, streamPerfMeasure } from '@/stores/utils/streamDebug';
import type { StreamPhase } from './message/types'; import type { StreamPhase } from './message/types';
import { normalizeParts } from './message/partUtils'; import { normalizeParts } from './message/partUtils';
import { useGlobalSessionsStore } from '@/stores/useGlobalSessionsStore';
import { getReviewTransferDirection, type ReviewTransferDirection } from '@/lib/reviewFlow';
import { getOriginalSessionID, getReviewSessionID } from '@/lib/sessionReviewMetadata';
const MESSAGE_LIST_VIRTUALIZE_THRESHOLD = 5; const MESSAGE_LIST_VIRTUALIZE_THRESHOLD = 5;
const MESSAGE_LIST_OVERSCAN = 6; const MESSAGE_LIST_OVERSCAN = 6;
@@ -444,6 +447,7 @@ interface MessageRowProps {
onContentChange: (reason?: ContentChangeReason) => void; onContentChange: (reason?: ContentChangeReason) => void;
animationHandlers: AnimationHandlers; animationHandlers: AnimationHandlers;
scrollToBottom?: () => void; scrollToBottom?: () => void;
reviewTransferDirection?: ReviewTransferDirection | null;
} }
const MessageRow = React.memo<MessageRowProps>(({ const MessageRow = React.memo<MessageRowProps>(({
@@ -459,6 +463,7 @@ const MessageRow = React.memo<MessageRowProps>(({
onContentChange, onContentChange,
animationHandlers, animationHandlers,
scrollToBottom, scrollToBottom,
reviewTransferDirection,
}) => { }) => {
return ( return (
<ChatMessage <ChatMessage
@@ -474,6 +479,7 @@ const MessageRow = React.memo<MessageRowProps>(({
assistantHeaderMessageId={assistantHeaderMessageId} assistantHeaderMessageId={assistantHeaderMessageId}
isInActiveTurn={isInActiveTurn} isInActiveTurn={isInActiveTurn}
activeStreamingPhase={activeStreamingPhase} activeStreamingPhase={activeStreamingPhase}
reviewTransferDirection={reviewTransferDirection}
/> />
); );
}, (prev, next) => { }, (prev, next) => {
@@ -491,6 +497,7 @@ const MessageRow = React.memo<MessageRowProps>(({
&& prev.assistantHeaderMessageId === next.assistantHeaderMessageId && prev.assistantHeaderMessageId === next.assistantHeaderMessageId
&& prev.isInActiveTurn === next.isInActiveTurn && prev.isInActiveTurn === next.isInActiveTurn
&& prev.activeStreamingPhase === next.activeStreamingPhase && prev.activeStreamingPhase === next.activeStreamingPhase
&& prev.reviewTransferDirection === next.reviewTransferDirection
&& prev.animationHandlers?.onChunk === next.animationHandlers?.onChunk && prev.animationHandlers?.onChunk === next.animationHandlers?.onChunk
&& prev.animationHandlers?.onComplete === next.animationHandlers?.onComplete && prev.animationHandlers?.onComplete === next.animationHandlers?.onComplete
&& prev.animationHandlers?.onStreamingCandidate === next.animationHandlers?.onStreamingCandidate && prev.animationHandlers?.onStreamingCandidate === next.animationHandlers?.onStreamingCandidate
@@ -518,6 +525,7 @@ interface TurnBlockProps {
onUserAnimationConsumed: (messageId: string) => void; onUserAnimationConsumed: (messageId: string) => void;
activeStreamingMessageId?: string | null; activeStreamingMessageId?: string | null;
activeStreamingPhase?: StreamPhase | null; activeStreamingPhase?: StreamPhase | null;
reviewTransferDirection?: ReviewTransferDirection | null;
} }
const TurnBlock = React.memo(({ const TurnBlock = React.memo(({
@@ -536,6 +544,7 @@ const TurnBlock = React.memo(({
onUserAnimationConsumed, onUserAnimationConsumed,
activeStreamingMessageId, activeStreamingMessageId,
activeStreamingPhase, activeStreamingPhase,
reviewTransferDirection,
}: TurnBlockProps) => { }: TurnBlockProps) => {
const turnUiState = turnUiStates.get(turn.turnId) ?? { isExpanded: defaultActivityExpanded }; const turnUiState = turnUiStates.get(turn.turnId) ?? { isExpanded: defaultActivityExpanded };
const handleToggleTurnGroup = React.useCallback(() => { const handleToggleTurnGroup = React.useCallback(() => {
@@ -756,6 +765,7 @@ const TurnBlock = React.memo(({
assistantHeaderMessageId={assistantHeaderMessageId} assistantHeaderMessageId={assistantHeaderMessageId}
isInActiveTurn={Boolean(streamingAssistantMessageId) && message.info.id === streamingAssistantMessageId} isInActiveTurn={Boolean(streamingAssistantMessageId) && message.info.id === streamingAssistantMessageId}
activeStreamingPhase={message.info.id === streamingAssistantMessageId ? activeStreamingPhase : null} activeStreamingPhase={message.info.id === streamingAssistantMessageId ? activeStreamingPhase : null}
reviewTransferDirection={reviewTransferDirection}
animateUserOnMount={shouldAnimateUserMessage(message)} animateUserOnMount={shouldAnimateUserMessage(message)}
onUserAnimationConsumed={onUserAnimationConsumed} onUserAnimationConsumed={onUserAnimationConsumed}
onContentChange={onMessageContentChange} onContentChange={onMessageContentChange}
@@ -782,6 +792,7 @@ const TurnBlock = React.memo(({
turnGroupingContextBase, turnGroupingContextBase,
streamingAssistantMessageId, streamingAssistantMessageId,
activeStreamingPhase, activeStreamingPhase,
reviewTransferDirection,
visibleAssistantMessages, visibleAssistantMessages,
visibleAssistantIds, visibleAssistantIds,
visibleActivitySegments, visibleActivitySegments,
@@ -820,6 +831,7 @@ interface UngroupedMessageRowProps {
onUserAnimationConsumed: (messageId: string) => void; onUserAnimationConsumed: (messageId: string) => void;
activeStreamingMessageId?: string | null; activeStreamingMessageId?: string | null;
activeStreamingPhase?: StreamPhase | null; activeStreamingPhase?: StreamPhase | null;
reviewTransferDirection?: ReviewTransferDirection | null;
} }
const UngroupedMessageRow = React.memo(({ const UngroupedMessageRow = React.memo(({
@@ -833,6 +845,7 @@ const UngroupedMessageRow = React.memo(({
onUserAnimationConsumed, onUserAnimationConsumed,
activeStreamingMessageId, activeStreamingMessageId,
activeStreamingPhase, activeStreamingPhase,
reviewTransferDirection,
}: UngroupedMessageRowProps) => { }: UngroupedMessageRowProps) => {
return ( return (
<MessageRow <MessageRow
@@ -846,6 +859,7 @@ const UngroupedMessageRow = React.memo(({
scrollToBottom={scrollToBottom} scrollToBottom={scrollToBottom}
isInActiveTurn={Boolean(activeStreamingMessageId) && message.info.id === activeStreamingMessageId} isInActiveTurn={Boolean(activeStreamingMessageId) && message.info.id === activeStreamingMessageId}
activeStreamingPhase={message.info.id === activeStreamingMessageId ? activeStreamingPhase : null} activeStreamingPhase={message.info.id === activeStreamingMessageId ? activeStreamingPhase : null}
reviewTransferDirection={reviewTransferDirection}
/> />
); );
}); });
@@ -867,6 +881,7 @@ interface MessageListEntryProps {
onUserAnimationConsumed: (messageId: string) => void; onUserAnimationConsumed: (messageId: string) => void;
activeStreamingMessageId?: string | null; activeStreamingMessageId?: string | null;
activeStreamingPhase?: StreamPhase | null; activeStreamingPhase?: StreamPhase | null;
reviewTransferDirection?: ReviewTransferDirection | null;
} }
const turnContainsMessageId = (turn: TurnRecord, messageId: string | null | undefined): boolean => { const turnContainsMessageId = (turn: TurnRecord, messageId: string | null | undefined): boolean => {
@@ -896,6 +911,7 @@ const MessageListEntry = React.memo(({
onUserAnimationConsumed, onUserAnimationConsumed,
activeStreamingMessageId, activeStreamingMessageId,
activeStreamingPhase, activeStreamingPhase,
reviewTransferDirection,
}: MessageListEntryProps) => { }: MessageListEntryProps) => {
if (entry.kind === 'ungrouped') { if (entry.kind === 'ungrouped') {
return ( return (
@@ -910,6 +926,7 @@ const MessageListEntry = React.memo(({
onUserAnimationConsumed={onUserAnimationConsumed} onUserAnimationConsumed={onUserAnimationConsumed}
activeStreamingMessageId={activeStreamingMessageId} activeStreamingMessageId={activeStreamingMessageId}
activeStreamingPhase={activeStreamingPhase} activeStreamingPhase={activeStreamingPhase}
reviewTransferDirection={reviewTransferDirection}
/> />
); );
} }
@@ -927,6 +944,7 @@ const MessageListEntry = React.memo(({
onUserAnimationConsumed={onUserAnimationConsumed} onUserAnimationConsumed={onUserAnimationConsumed}
activeStreamingMessageId={activeStreamingMessageId} activeStreamingMessageId={activeStreamingMessageId}
activeStreamingPhase={activeStreamingPhase} activeStreamingPhase={activeStreamingPhase}
reviewTransferDirection={reviewTransferDirection}
onMessageContentChange={onMessageContentChange} onMessageContentChange={onMessageContentChange}
getAnimationHandlers={getAnimationHandlers} getAnimationHandlers={getAnimationHandlers}
scrollToBottom={scrollToBottom} scrollToBottom={scrollToBottom}
@@ -956,9 +974,10 @@ type StaticHistoryListProps = {
shouldAnimateUserMessage: (message: ChatMessageEntry) => boolean; shouldAnimateUserMessage: (message: ChatMessageEntry) => boolean;
onUserAnimationConsumed: (messageId: string) => void; onUserAnimationConsumed: (messageId: string) => void;
activeStreamingPhase?: StreamPhase | null; activeStreamingPhase?: StreamPhase | null;
reviewTransferDirection?: ReviewTransferDirection | null;
}; };
const StaticHistoryList = React.memo(({ entries, shouldVirtualize, virtualRows, totalSize, measureElement, contentRef, onMessageContentChange, getAnimationHandlers, scrollToBottom, stickyUserHeader, defaultActivityExpanded, turnUiStates, onToggleTurnGroup, chatRenderMode, shouldAnimateUserMessage, onUserAnimationConsumed, activeStreamingPhase }: StaticHistoryListProps) => { const StaticHistoryList = React.memo(({ entries, shouldVirtualize, virtualRows, totalSize, measureElement, contentRef, onMessageContentChange, getAnimationHandlers, scrollToBottom, stickyUserHeader, defaultActivityExpanded, turnUiStates, onToggleTurnGroup, chatRenderMode, shouldAnimateUserMessage, onUserAnimationConsumed, activeStreamingPhase, reviewTransferDirection }: StaticHistoryListProps) => {
const renderEntry = React.useCallback((entry: RenderEntry) => { const renderEntry = React.useCallback((entry: RenderEntry) => {
return ( return (
<MessageListEntry <MessageListEntry
@@ -977,9 +996,10 @@ const StaticHistoryList = React.memo(({ entries, shouldVirtualize, virtualRows,
onUserAnimationConsumed={onUserAnimationConsumed} onUserAnimationConsumed={onUserAnimationConsumed}
activeStreamingMessageId={null} activeStreamingMessageId={null}
activeStreamingPhase={activeStreamingPhase} activeStreamingPhase={activeStreamingPhase}
reviewTransferDirection={reviewTransferDirection}
/> />
); );
}, [activeStreamingPhase, chatRenderMode, defaultActivityExpanded, getAnimationHandlers, onMessageContentChange, onToggleTurnGroup, onUserAnimationConsumed, scrollToBottom, shouldAnimateUserMessage, stickyUserHeader, turnUiStates]); }, [activeStreamingPhase, chatRenderMode, defaultActivityExpanded, getAnimationHandlers, onMessageContentChange, onToggleTurnGroup, onUserAnimationConsumed, reviewTransferDirection, scrollToBottom, shouldAnimateUserMessage, stickyUserHeader, turnUiStates]);
const paddingTop = shouldVirtualize && virtualRows.length > 0 const paddingTop = shouldVirtualize && virtualRows.length > 0
? virtualRows[0]?.start ?? 0 ? virtualRows[0]?.start ?? 0
@@ -1060,6 +1080,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;
reviewTransferDirection?: ReviewTransferDirection | null;
}> = ({ }> = ({
entry, entry,
onMessageContentChange, onMessageContentChange,
@@ -1075,6 +1096,7 @@ const StreamingTailContent: React.FC<{
onUserAnimationConsumed, onUserAnimationConsumed,
activeStreamingMessageId, activeStreamingMessageId,
activeStreamingPhase, activeStreamingPhase,
reviewTransferDirection,
}) => { }) => {
return ( return (
<MessageListEntry <MessageListEntry
@@ -1092,6 +1114,7 @@ const StreamingTailContent: React.FC<{
onUserAnimationConsumed={onUserAnimationConsumed} onUserAnimationConsumed={onUserAnimationConsumed}
activeStreamingMessageId={activeStreamingMessageId} activeStreamingMessageId={activeStreamingMessageId}
activeStreamingPhase={activeStreamingPhase} activeStreamingPhase={activeStreamingPhase}
reviewTransferDirection={reviewTransferDirection}
/> />
); );
}; };
@@ -1118,6 +1141,20 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
const activityRenderMode = useUIStore((state) => state.activityRenderMode); const activityRenderMode = useUIStore((state) => state.activityRenderMode);
const showTurnChangedFiles = useUIStore((state) => state.showTurnChangedFiles); const showTurnChangedFiles = useUIStore((state) => state.showTurnChangedFiles);
const defaultActivityExpanded = activityRenderMode === 'summary'; const defaultActivityExpanded = activityRenderMode === 'summary';
const reviewTransferDirection = useGlobalSessionsStore((state) => {
const currentSession = state.activeSessions.find((session) => session.id === sessionKey);
const direction = getReviewTransferDirection(currentSession);
if (!currentSession || !direction) return null;
const targetSessionId = direction === 'review-to-original'
? getOriginalSessionID(currentSession)
: getReviewSessionID(currentSession);
if (!targetSessionId) return null;
return state.activeSessions.some((session) => session.id === targetSessionId)
? direction
: null;
});
const [turnUiStates, setTurnUiStates] = React.useState<Map<string, TurnUiState>>(() => new Map()); const [turnUiStates, setTurnUiStates] = React.useState<Map<string, TurnUiState>>(() => new Map());
const userAnimationRef = React.useRef<{ const userAnimationRef = React.useRef<{
sessionKey: string | undefined; sessionKey: string | undefined;
@@ -1692,6 +1729,7 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
shouldAnimateUserMessage={shouldAnimateUserMessage} shouldAnimateUserMessage={shouldAnimateUserMessage}
onUserAnimationConsumed={onUserAnimationConsumed} onUserAnimationConsumed={onUserAnimationConsumed}
activeStreamingPhase={activeStreamingPhase} activeStreamingPhase={activeStreamingPhase}
reviewTransferDirection={reviewTransferDirection}
/> />
{trailingStreamingEntry ? ( {trailingStreamingEntry ? (
<StreamingTailContent <StreamingTailContent
@@ -1709,6 +1747,7 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
onUserAnimationConsumed={onUserAnimationConsumed} onUserAnimationConsumed={onUserAnimationConsumed}
activeStreamingMessageId={activeStreamingMessageId} activeStreamingMessageId={activeStreamingMessageId}
activeStreamingPhase={activeStreamingPhase} activeStreamingPhase={activeStreamingPhase}
reviewTransferDirection={reviewTransferDirection}
/> />
) : null} ) : null}
</div> </div>
@@ -48,9 +48,8 @@ import { useI18n } from '@/lib/i18n';
import { extractLoopbackUrls } from '@/lib/url'; import { extractLoopbackUrls } from '@/lib/url';
import { useDeviceInfo } from '@/lib/device'; import { useDeviceInfo } from '@/lib/device';
import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
import { useGlobalSessionsStore } from '@/stores/useGlobalSessionsStore';
import { import {
getReviewTransferDirection, type ReviewTransferDirection,
sendImplementationResponseToReviewer, sendImplementationResponseToReviewer,
sendReviewFeedbackToOriginal, sendReviewFeedbackToOriginal,
} from '@/lib/reviewFlow'; } from '@/lib/reviewFlow';
@@ -366,6 +365,7 @@ interface MessageBodyProps {
errorVariant?: 'error' | 'info'; errorVariant?: 'error' | 'info';
userActionsMode?: 'inline' | 'external-content' | 'external-actions'; userActionsMode?: 'inline' | 'external-content' | 'external-actions';
stickyUserHeaderEnabled?: boolean; stickyUserHeaderEnabled?: boolean;
reviewTransferDirection?: ReviewTransferDirection | null;
} }
const TOOL_REVEAL_CACHE_MAX = 200; const TOOL_REVEAL_CACHE_MAX = 200;
@@ -967,6 +967,7 @@ const AssistantMessageBody = React.memo(({
turnGroupingContext, turnGroupingContext,
errorMessage, errorMessage,
errorVariant = 'error', errorVariant = 'error',
reviewTransferDirection = null,
}: Omit<MessageBodyProps, 'isUser'>) => { }: Omit<MessageBodyProps, 'isUser'>) => {
const { t } = useI18n(); const { t } = useI18n();
const chatSurfaceMode = useChatSurfaceMode(); const chatSurfaceMode = useChatSurfaceMode();
@@ -1121,26 +1122,15 @@ const AssistantMessageBody = React.memo(({
const createSessionFromAssistantMessage = useSessionUIStore((state) => state.createSessionFromAssistantMessage); const createSessionFromAssistantMessage = useSessionUIStore((state) => state.createSessionFromAssistantMessage);
const currentSessionId = useSessionUIStore((state) => state.currentSessionId); const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
const getDirectoryForSession = useSessionUIStore((state) => state.getDirectoryForSession); const getDirectoryForSession = useSessionUIStore((state) => state.getDirectoryForSession);
const currentSession = useGlobalSessionsStore((state) => {
if (!sessionId) return null;
for (const candidate of state.activeSessions) {
if (candidate.id === sessionId) return candidate;
}
for (const candidate of state.archivedSessions) {
if (candidate.id === sessionId) return candidate;
}
return null;
});
const openMultiRunLauncherWithPrompt = useUIStore((state) => state.openMultiRunLauncherWithPrompt); const openMultiRunLauncherWithPrompt = useUIStore((state) => state.openMultiRunLauncherWithPrompt);
const projects = useProjectsStore((state) => state.projects); const projects = useProjectsStore((state) => state.projects);
const effectiveDirectory = useEffectiveDirectory(); const effectiveDirectory = useEffectiveDirectory();
const currentReviewTransferDirection = getReviewTransferDirection(currentSession); const isReviewSessionView = reviewTransferDirection === 'review-to-original';
const isReviewSessionView = currentReviewTransferDirection === 'review-to-original'; const effectiveReviewTransferDirection = (!isMobile && !isVSCode) ? reviewTransferDirection : null;
const reviewTransferDirection = (!isMobile && !isVSCode) ? currentReviewTransferDirection : null;
const reviewTransferAction = React.useMemo(() => { const reviewTransferAction = React.useMemo(() => {
const transferText = assistantPlanText.trim(); const transferText = assistantPlanText.trim();
if (!sessionId || !effectiveDirectory || !transferText || !reviewTransferDirection) return undefined; if (!sessionId || !effectiveDirectory || !transferText || !effectiveReviewTransferDirection) return undefined;
if (reviewTransferDirection === 'review-to-original') { if (effectiveReviewTransferDirection === 'review-to-original') {
return { return {
ariaLabel: t('chat.messageBody.actions.sendReviewFeedback'), ariaLabel: t('chat.messageBody.actions.sendReviewFeedback'),
tooltip: t('chat.messageBody.actions.sendReviewFeedback'), tooltip: t('chat.messageBody.actions.sendReviewFeedback'),
@@ -1164,7 +1154,7 @@ const AssistantMessageBody = React.memo(({
} }
}, },
}; };
}, [assistantPlanText, effectiveDirectory, reviewTransferDirection, sessionId, t]); }, [assistantPlanText, effectiveDirectory, effectiveReviewTransferDirection, sessionId, t]);
const [isPlanDialogOpen, setIsPlanDialogOpen] = React.useState(false); const [isPlanDialogOpen, setIsPlanDialogOpen] = React.useState(false);
const [isSavingPlan, setIsSavingPlan] = React.useState(false); const [isSavingPlan, setIsSavingPlan] = React.useState(false);
const [isForkDialogOpen, setIsForkDialogOpen] = React.useState(false); const [isForkDialogOpen, setIsForkDialogOpen] = React.useState(false);
+3 -1
View File
@@ -226,7 +226,9 @@ export const sendImplementationResponseToReviewer = async (originalSessionID: st
openReviewSessionPanel(directory, reviewSession); openReviewSessionPanel(directory, reviewSession);
}; };
export const getReviewTransferDirection = (session: Session | null | undefined): 'review-to-original' | 'original-to-review' | null => { export type ReviewTransferDirection = 'review-to-original' | 'original-to-review';
export const getReviewTransferDirection = (session: Session | null | undefined): ReviewTransferDirection | null => {
if (isReviewSession(session)) return 'review-to-original'; if (isReviewSession(session)) return 'review-to-original';
if (getReviewSessionID(session)) return 'original-to-review'; if (getReviewSessionID(session)) return 'original-to-review';
return null; return null;