From d459b03e842b02bc3825e9785a5f23e0b87d31c5 Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Tue, 3 Jun 2025 00:32:42 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(message):=20prevent=20histor?= =?UTF-8?q?y=20loss=20when=20retrying=20imported=20messages=20with=20dupli?= =?UTF-8?q?cate=20IDs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use object reference comparison first before falling back to ID matching - Prevent incorrect message index lookup when duplicate IDs exist - Apply fix to both handleMessageReset and handleMessageDelete functions - Maintain backward compatibility with ID-based message identification Previously, when importing messages that contained duplicate IDs, the findIndex operation would match the first occurrence rather than the intended message, causing history truncation on retry. This change uses object reference comparison as the primary method, ensuring accurate message identification and preserving conversation history. --- .../components/playground/MessageActions.js | 10 +++++----- web/src/hooks/useMessageActions.js | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/web/src/components/playground/MessageActions.js b/web/src/components/playground/MessageActions.js index 1fe40691..9f42aeb7 100644 --- a/web/src/components/playground/MessageActions.js +++ b/web/src/components/playground/MessageActions.js @@ -41,7 +41,7 @@ const MessageActions = ({ icon={} onClick={() => !shouldDisableActions && onMessageReset(message)} disabled={shouldDisableActions} - className={`!rounded-md ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : '!text-gray-400 hover:!text-blue-600 hover:!bg-blue-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} + className={`!rounded-full ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : '!text-gray-400 hover:!text-blue-600 hover:!bg-blue-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} aria-label={t('重试')} /> @@ -55,7 +55,7 @@ const MessageActions = ({ size="small" icon={} onClick={() => onMessageCopy(message)} - className={`!rounded-md !text-gray-400 hover:!text-green-600 hover:!bg-green-50 ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} + className={`!rounded-full !text-gray-400 hover:!text-green-600 hover:!bg-green-50 ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} aria-label={t('复制')} /> @@ -70,7 +70,7 @@ const MessageActions = ({ icon={} onClick={() => !shouldDisableActions && onMessageEdit(message)} disabled={shouldDisableActions} - className={`!rounded-md ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : '!text-gray-400 hover:!text-yellow-600 hover:!bg-yellow-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} + className={`!rounded-full ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : '!text-gray-400 hover:!text-yellow-600 hover:!bg-yellow-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} aria-label={t('编辑')} /> @@ -94,7 +94,7 @@ const MessageActions = ({ icon={} onClick={() => !shouldDisableActions && onRoleToggle && onRoleToggle(message)} disabled={shouldDisableActions} - className={`!rounded-md ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : message.role === 'system' ? '!text-purple-500 hover:!text-purple-700 hover:!bg-purple-50' : '!text-gray-400 hover:!text-purple-600 hover:!bg-purple-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} + className={`!rounded-full ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : message.role === 'system' ? '!text-purple-500 hover:!text-purple-700 hover:!bg-purple-50' : '!text-gray-400 hover:!text-purple-600 hover:!bg-purple-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} aria-label={message.role === 'assistant' ? t('切换为System角色') : t('切换为Assistant角色')} /> @@ -109,7 +109,7 @@ const MessageActions = ({ icon={} onClick={() => !shouldDisableActions && onMessageDelete(message)} disabled={shouldDisableActions} - className={`!rounded-md ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : '!text-gray-400 hover:!text-red-600 hover:!bg-red-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} + className={`!rounded-full ${shouldDisableActions ? '!text-gray-300 !cursor-not-allowed' : '!text-gray-400 hover:!text-red-600 hover:!bg-red-50'} ${styleState.isMobile ? '!w-6 !h-6' : '!w-7 !h-7'} !p-0 transition-all`} aria-label={t('删除')} /> diff --git a/web/src/hooks/useMessageActions.js b/web/src/hooks/useMessageActions.js index d67618ce..84d94d4d 100644 --- a/web/src/hooks/useMessageActions.js +++ b/web/src/hooks/useMessageActions.js @@ -88,7 +88,14 @@ export const useMessageActions = (message, setMessage, onMessageSend, saveMessag // 重新生成消息 const handleMessageReset = useCallback((targetMessage) => { setMessage(prevMessages => { - const messageIndex = prevMessages.findIndex(msg => msg.id === targetMessage.id); + // 使用引用查找索引,防止重复 id 造成误匹配 + let messageIndex = prevMessages.findIndex(msg => msg === targetMessage); + + // 回退到 id 匹配(兼容不同引用场景) + if (messageIndex === -1) { + messageIndex = prevMessages.findIndex(msg => msg.id === targetMessage.id); + } + if (messageIndex === -1) return prevMessages; if (targetMessage.role === 'user') { @@ -135,7 +142,14 @@ export const useMessageActions = (message, setMessage, onMessageSend, saveMessag }, onOk: () => { setMessage(prevMessages => { - const messageIndex = prevMessages.findIndex(msg => msg.id === targetMessage.id); + // 使用引用查找索引,防止重复 id 造成误匹配 + let messageIndex = prevMessages.findIndex(msg => msg === targetMessage); + + // 回退到 id 匹配(兼容不同引用场景) + if (messageIndex === -1) { + messageIndex = prevMessages.findIndex(msg => msg.id === targetMessage.id); + } + if (messageIndex === -1) return prevMessages; let updatedMessages;