chore: test deploy snapshot
This commit is contained in:
@@ -3049,6 +3049,19 @@ export function MainChatPanel({
|
|||||||
const requestItems = Array.isArray(requestItemsState) ? requestItemsState : [];
|
const requestItems = Array.isArray(requestItemsState) ? requestItemsState : [];
|
||||||
const isCreatingImportedDraftConversationRef = useRef(false);
|
const isCreatingImportedDraftConversationRef = useRef(false);
|
||||||
const hasAttemptedInitialConversationRef = useRef(false);
|
const hasAttemptedInitialConversationRef = useRef(false);
|
||||||
|
const pendingConversationSummaryUpdatesRef = useRef(
|
||||||
|
new Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
incomingMessage: ChatMessage;
|
||||||
|
hasMeaningfulCodexResponse: boolean;
|
||||||
|
hasPendingAttentionResponse: boolean;
|
||||||
|
isForegroundSession: boolean;
|
||||||
|
responseTimestamp: string;
|
||||||
|
}
|
||||||
|
>(),
|
||||||
|
);
|
||||||
|
const conversationSummaryUpdateFrameRef = useRef<number | null>(null);
|
||||||
const setDraft = useCallback((value: SetStateAction<string>) => {
|
const setDraft = useCallback((value: SetStateAction<string>) => {
|
||||||
const nextValue = typeof value === 'function' ? value(draftRef.current) : value;
|
const nextValue = typeof value === 'function' ? value(draftRef.current) : value;
|
||||||
draftRef.current = nextValue;
|
draftRef.current = nextValue;
|
||||||
@@ -3066,6 +3079,63 @@ export function MainChatPanel({
|
|||||||
const setDraftValue = useCallback((value: string) => {
|
const setDraftValue = useCallback((value: string) => {
|
||||||
setDraft(value);
|
setDraft(value);
|
||||||
}, [setDraft]);
|
}, [setDraft]);
|
||||||
|
|
||||||
|
const enqueueConversationSummaryUpdate = useCallback(
|
||||||
|
(payload: {
|
||||||
|
sessionId: string;
|
||||||
|
incomingMessage: ChatMessage;
|
||||||
|
hasMeaningfulCodexResponse: boolean;
|
||||||
|
hasPendingAttentionResponse: boolean;
|
||||||
|
isForegroundSession: boolean;
|
||||||
|
responseTimestamp: string;
|
||||||
|
}) => {
|
||||||
|
pendingConversationSummaryUpdatesRef.current.set(payload.sessionId, payload);
|
||||||
|
|
||||||
|
if (conversationSummaryUpdateFrameRef.current !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conversationSummaryUpdateFrameRef.current = window.requestAnimationFrame(() => {
|
||||||
|
conversationSummaryUpdateFrameRef.current = null;
|
||||||
|
const batchedUpdates = new Map(pendingConversationSummaryUpdatesRef.current);
|
||||||
|
pendingConversationSummaryUpdatesRef.current.clear();
|
||||||
|
|
||||||
|
if (batchedUpdates.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setConversationItems((previous) =>
|
||||||
|
sortChatConversationSummaries(
|
||||||
|
previous.map((item) => {
|
||||||
|
const update = batchedUpdates.get(item.sessionId);
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
lastMessagePreview: createConversationPreviewText(update.incomingMessage.text),
|
||||||
|
lastResponsePreview:
|
||||||
|
update.incomingMessage.author === 'codex' && !isPreparingChatReplyText(update.incomingMessage.text)
|
||||||
|
? createConversationPreviewText(update.incomingMessage.text)
|
||||||
|
: item.lastResponsePreview,
|
||||||
|
lastMessageAt: update.responseTimestamp,
|
||||||
|
updatedAt: update.responseTimestamp,
|
||||||
|
hasUnreadResponse:
|
||||||
|
update.hasMeaningfulCodexResponse && !update.isForegroundSession ? true : item.hasUnreadResponse,
|
||||||
|
hasPendingAttention:
|
||||||
|
update.hasPendingAttentionResponse && update.incomingMessage.author === 'codex'
|
||||||
|
? true
|
||||||
|
: item.hasPendingAttention,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setConversationItems],
|
||||||
|
);
|
||||||
const setRequestItems = useCallback((next: SetStateAction<ChatConversationRequest[]>) => {
|
const setRequestItems = useCallback((next: SetStateAction<ChatConversationRequest[]>) => {
|
||||||
setRequestItemsState((previous) => {
|
setRequestItemsState((previous) => {
|
||||||
const safePrevious = Array.isArray(previous) ? previous : [];
|
const safePrevious = Array.isArray(previous) ? previous : [];
|
||||||
@@ -3092,6 +3162,14 @@ export function MainChatPanel({
|
|||||||
activeSessionIdRef.current = activeSessionId;
|
activeSessionIdRef.current = activeSessionId;
|
||||||
}, [activeSessionId]);
|
}, [activeSessionId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (conversationSummaryUpdateFrameRef.current !== null) {
|
||||||
|
window.cancelAnimationFrame(conversationSummaryUpdateFrameRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mode !== 'live' || location.pathname !== chatRoutePath) {
|
if (mode !== 'live' || location.pathname !== chatRoutePath) {
|
||||||
return;
|
return;
|
||||||
@@ -4337,28 +4415,14 @@ export function MainChatPanel({
|
|||||||
});
|
});
|
||||||
const responseTimestamp = new Date().toISOString();
|
const responseTimestamp = new Date().toISOString();
|
||||||
|
|
||||||
setConversationItems((previous) =>
|
enqueueConversationSummaryUpdate({
|
||||||
sortChatConversationSummaries(
|
sessionId,
|
||||||
previous.map((item) =>
|
incomingMessage,
|
||||||
item.sessionId === sessionId
|
hasMeaningfulCodexResponse,
|
||||||
? {
|
hasPendingAttentionResponse,
|
||||||
...item,
|
isForegroundSession,
|
||||||
lastMessagePreview: createConversationPreviewText(incomingMessage.text),
|
responseTimestamp,
|
||||||
lastResponsePreview:
|
});
|
||||||
incomingMessage.author === 'codex' && !isPreparingChatReplyText(incomingMessage.text)
|
|
||||||
? createConversationPreviewText(incomingMessage.text)
|
|
||||||
: item.lastResponsePreview,
|
|
||||||
lastMessageAt: responseTimestamp,
|
|
||||||
updatedAt: responseTimestamp,
|
|
||||||
hasUnreadResponse:
|
|
||||||
hasMeaningfulCodexResponse && !isForegroundSession ? true : item.hasUnreadResponse,
|
|
||||||
hasPendingAttention:
|
|
||||||
hasPendingAttentionResponse && incomingMessage.author === 'codex' ? true : item.hasPendingAttention,
|
|
||||||
}
|
|
||||||
: item,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const eventConversation = conversationItemsRef.current.find((item) => item.sessionId === sessionId) ?? null;
|
const eventConversation = conversationItemsRef.current.find((item) => item.sessionId === sessionId) ?? null;
|
||||||
|
|
||||||
|
|||||||
@@ -521,6 +521,12 @@ type MessageRenderPayload = {
|
|||||||
promptTargets: Extract<ChatMessagePart, { type: 'prompt' }>[];
|
promptTargets: Extract<ChatMessagePart, { type: 'prompt' }>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type MessageRenderPayloadCacheEntry = {
|
||||||
|
messageText: string;
|
||||||
|
messageParts: ChatMessagePart[] | undefined;
|
||||||
|
payload: MessageRenderPayload;
|
||||||
|
};
|
||||||
|
|
||||||
const RANK_LINE_PATTERN = /(?:\b(?:rank|score)\b|랭크|점수)\s*[:=]?\s*[-+]?\d+(?:\.\d+)?(?:e[-+]?\d+)?\b/i;
|
const RANK_LINE_PATTERN = /(?:\b(?:rank|score)\b|랭크|점수)\s*[:=]?\s*[-+]?\d+(?:\.\d+)?(?:e[-+]?\d+)?\b/i;
|
||||||
const TITLE_VALUE_PATTERN = /^(?:[-*]\s*)?(?:\d+\.\s*)?(?:title|제목)\s*[:=-]\s*(.+)$/i;
|
const TITLE_VALUE_PATTERN = /^(?:[-*]\s*)?(?:\d+\.\s*)?(?:title|제목)\s*[:=-]\s*(.+)$/i;
|
||||||
const LINK_VALUE_PATTERN = /^(?:[-*]\s*)?(?:\d+\.\s*)?(?:link|url|href|링크)\s*[:=-]\s*(https?:\/\/\S+|\/\S+)$/i;
|
const LINK_VALUE_PATTERN = /^(?:[-*]\s*)?(?:\d+\.\s*)?(?:link|url|href|링크)\s*[:=-]\s*(https?:\/\/\S+|\/\S+)$/i;
|
||||||
@@ -3654,6 +3660,7 @@ export function ChatConversationView({
|
|||||||
const suppressImmediateSendClickRef = useRef(false);
|
const suppressImmediateSendClickRef = useRef(false);
|
||||||
const composerDraftValueRef = useRef(draft);
|
const composerDraftValueRef = useRef(draft);
|
||||||
const lastSyncedComposerDraftRef = useRef(draft);
|
const lastSyncedComposerDraftRef = useRef(draft);
|
||||||
|
const messageRenderPayloadCacheRef = useRef(new Map<number, MessageRenderPayloadCacheEntry>());
|
||||||
|
|
||||||
const setComposerDraftValue = (nextValue: string) => {
|
const setComposerDraftValue = (nextValue: string) => {
|
||||||
composerDraftValueRef.current = nextValue;
|
composerDraftValueRef.current = nextValue;
|
||||||
@@ -3788,6 +3795,10 @@ export function ChatConversationView({
|
|||||||
forceComposerDraftSync();
|
forceComposerDraftSync();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
messageRenderPayloadCacheRef.current.clear();
|
||||||
|
}, [sessionId]);
|
||||||
|
|
||||||
const shouldShowConversationLoadingOverlay = isConversationLoading && visibleMessages.length === 0;
|
const shouldShowConversationLoadingOverlay = isConversationLoading && visibleMessages.length === 0;
|
||||||
|
|
||||||
const orderedMessages = useMemo(() => {
|
const orderedMessages = useMemo(() => {
|
||||||
@@ -3954,14 +3965,44 @@ export function ChatConversationView({
|
|||||||
|
|
||||||
setExpandedSystemExecutionActivityRequestId(null);
|
setExpandedSystemExecutionActivityRequestId(null);
|
||||||
}, [systemExecutionDisplayMode]);
|
}, [systemExecutionDisplayMode]);
|
||||||
const messageRenderPayloadById = useMemo(
|
const getCachedMessageRenderPayload = useCallback(
|
||||||
() =>
|
(message: ChatMessage) => {
|
||||||
new Map(
|
const cached = messageRenderPayloadCacheRef.current.get(message.id);
|
||||||
orderedMessages.map((message) => [message.id, extractMessageRenderPayload(message)] as const),
|
|
||||||
),
|
if (cached && cached.messageText === message.text && cached.messageParts === message.parts) {
|
||||||
[orderedMessages],
|
return cached.payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = extractMessageRenderPayload(message);
|
||||||
|
messageRenderPayloadCacheRef.current.set(message.id, {
|
||||||
|
messageText: message.text,
|
||||||
|
messageParts: message.parts,
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
return payload;
|
||||||
|
},
|
||||||
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const messageRenderPayloadById = useMemo(() => {
|
||||||
|
const activeMessageIds = new Set<number>();
|
||||||
|
const nextMap = new Map<number, MessageRenderPayload>();
|
||||||
|
|
||||||
|
orderedMessages.forEach((message) => {
|
||||||
|
activeMessageIds.add(message.id);
|
||||||
|
nextMap.set(message.id, getCachedMessageRenderPayload(message));
|
||||||
|
});
|
||||||
|
|
||||||
|
const cachedMessageIds = Array.from(messageRenderPayloadCacheRef.current.keys());
|
||||||
|
cachedMessageIds.forEach((messageId) => {
|
||||||
|
if (!activeMessageIds.has(messageId)) {
|
||||||
|
messageRenderPayloadCacheRef.current.delete(messageId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return nextMap;
|
||||||
|
}, [orderedMessages, getCachedMessageRenderPayload]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOpenedPreviewUrls([]);
|
setOpenedPreviewUrls([]);
|
||||||
setExpandedGroupIds([]);
|
setExpandedGroupIds([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user