mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-15 21:27:35 +00:00
refactor(ChatMessagesPane): use stable message key
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useCallback, useRef } from 'react';
|
||||||
import type { Dispatch, RefObject, SetStateAction } from 'react';
|
import type { Dispatch, RefObject, SetStateAction } from 'react';
|
||||||
import SessionProviderLogo from '../../../SessionProviderLogo';
|
import SessionProviderLogo from '../../../SessionProviderLogo';
|
||||||
import MessageComponent from './MessageComponent';
|
import MessageComponent from './MessageComponent';
|
||||||
@@ -45,6 +46,42 @@ interface ChatMessagesPaneProps {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toMessageKeyPart = (value: unknown): string | null => {
|
||||||
|
if (typeof value !== 'string' && typeof value !== 'number') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const normalized = String(value).trim();
|
||||||
|
return normalized.length > 0 ? normalized : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIntrinsicMessageKey = (message: ChatMessage): string | null => {
|
||||||
|
const candidates = [
|
||||||
|
message.id,
|
||||||
|
message.messageId,
|
||||||
|
message.toolId,
|
||||||
|
message.toolCallId,
|
||||||
|
message.blobId,
|
||||||
|
message.rowid,
|
||||||
|
message.sequence,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
const keyPart = toMessageKeyPart(candidate);
|
||||||
|
if (keyPart) {
|
||||||
|
return `message-${message.type}-${keyPart}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = new Date(message.timestamp).getTime();
|
||||||
|
if (!Number.isFinite(timestamp)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentPreview = typeof message.content === 'string' ? message.content.slice(0, 48) : '';
|
||||||
|
const toolName = typeof message.toolName === 'string' ? message.toolName : '';
|
||||||
|
return `message-${message.type}-${timestamp}-${toolName}-${contentPreview}`;
|
||||||
|
};
|
||||||
|
|
||||||
function AssistantThinkingIndicator() {
|
function AssistantThinkingIndicator() {
|
||||||
const selectedProvider = (localStorage.getItem('selected-provider') || 'claude') as Provider;
|
const selectedProvider = (localStorage.getItem('selected-provider') || 'claude') as Provider;
|
||||||
|
|
||||||
@@ -115,6 +152,33 @@ export default function ChatMessagesPane({
|
|||||||
isLoading,
|
isLoading,
|
||||||
}: ChatMessagesPaneProps) {
|
}: ChatMessagesPaneProps) {
|
||||||
const { t } = useTranslation('chat');
|
const { t } = useTranslation('chat');
|
||||||
|
const messageKeyMapRef = useRef<WeakMap<ChatMessage, string>>(new WeakMap());
|
||||||
|
const allocatedKeysRef = useRef<Set<string>>(new Set());
|
||||||
|
const generatedMessageKeyCounterRef = useRef(0);
|
||||||
|
|
||||||
|
// Keep keys stable across prepends so existing MessageComponent instances retain local state.
|
||||||
|
const getMessageKey = useCallback((message: ChatMessage) => {
|
||||||
|
const existingKey = messageKeyMapRef.current.get(message);
|
||||||
|
if (existingKey) {
|
||||||
|
return existingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const intrinsicKey = getIntrinsicMessageKey(message);
|
||||||
|
let candidateKey = intrinsicKey;
|
||||||
|
|
||||||
|
if (!candidateKey || allocatedKeysRef.current.has(candidateKey)) {
|
||||||
|
do {
|
||||||
|
generatedMessageKeyCounterRef.current += 1;
|
||||||
|
candidateKey = intrinsicKey
|
||||||
|
? `${intrinsicKey}-${generatedMessageKeyCounterRef.current}`
|
||||||
|
: `message-generated-${generatedMessageKeyCounterRef.current}`;
|
||||||
|
} while (allocatedKeysRef.current.has(candidateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedKeysRef.current.add(candidateKey);
|
||||||
|
messageKeyMapRef.current.set(message, candidateKey);
|
||||||
|
return candidateKey;
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -183,7 +247,7 @@ export default function ChatMessagesPane({
|
|||||||
const prevMessage = index > 0 ? visibleMessages[index - 1] : null;
|
const prevMessage = index > 0 ? visibleMessages[index - 1] : null;
|
||||||
return (
|
return (
|
||||||
<MessageComponent
|
<MessageComponent
|
||||||
key={index}
|
key={getMessageKey(message)}
|
||||||
message={message}
|
message={message}
|
||||||
index={index}
|
index={index}
|
||||||
prevMessage={prevMessage}
|
prevMessage={prevMessage}
|
||||||
|
|||||||
Reference in New Issue
Block a user