Preserve stable snapshots and stabilize Electron e2e (#734)

This commit is contained in:
Lingxuan Zuo
2026-04-01 20:35:01 +08:00
committed by GitHub
Unverified
parent 34bbb039d3
commit 5a3da41562
21 changed files with 758 additions and 78 deletions

View File

@@ -37,7 +37,45 @@ export function createHistoryActions(
const { currentSessionKey } = get();
if (!quiet) set({ loading: true, error: null });
const isCurrentSession = () => get().currentSessionKey === currentSessionKey;
const getPreviewMergeKey = (message: RawMessage): string => (
`${message.id ?? ''}|${message.role}|${message.timestamp ?? ''}|${getMessageText(message.content)}`
);
const mergeHydratedMessages = (
currentMessages: RawMessage[],
hydratedMessages: RawMessage[],
): RawMessage[] => {
const hydratedFilesByKey = new Map(
hydratedMessages
.filter((message) => message._attachedFiles?.length)
.map((message) => [
getPreviewMergeKey(message),
message._attachedFiles!.map((file) => ({ ...file })),
]),
);
return currentMessages.map((message) => {
const attachedFiles = hydratedFilesByKey.get(getPreviewMergeKey(message));
return attachedFiles
? { ...message, _attachedFiles: attachedFiles }
: message;
});
};
const applyLoadFailure = (errorMessage: string | null) => {
if (!isCurrentSession()) return;
set((state) => {
const hasMessages = state.messages.length > 0;
return {
loading: false,
error: !quiet && errorMessage ? errorMessage : state.error,
...(hasMessages ? {} : { messages: [] as RawMessage[] }),
};
});
};
const applyLoadedMessages = (rawMessages: RawMessage[], thinkingLevel: string | null) => {
if (!isCurrentSession()) return;
// Before filtering: attach images/files from tool_result messages to the next assistant message
const messagesWithToolImages = enrichWithToolResultFiles(rawMessages);
const filteredMessages = messagesWithToolImages.filter((msg) => !isToolResultRole(msg.role) && !isInternalMessage(msg));
@@ -95,17 +133,11 @@ export function createHistoryActions(
// Async: load missing image previews from disk (updates in background)
loadMissingPreviews(finalMessages).then((updated) => {
if (!isCurrentSession()) return;
if (updated) {
// Create new object references so React.memo detects changes.
// loadMissingPreviews mutates AttachedFileMeta in place, so we
// must produce fresh message + file references for each affected msg.
set({
messages: finalMessages.map(msg =>
msg._attachedFiles
? { ...msg, _attachedFiles: msg._attachedFiles.map(f => ({ ...f })) }
: msg
),
});
set((state) => ({
messages: mergeHydratedMessages(state.messages, finalMessages),
}));
}
});
const { pendingFinal, lastUserMessageAt, sending: isSendingNow } = get();
@@ -163,7 +195,7 @@ export function createHistoryActions(
if (fallbackMessages.length > 0) {
applyLoadedMessages(fallbackMessages, null);
} else {
set({ messages: [], loading: false });
applyLoadFailure(result.error || 'Failed to load chat history');
}
}
} catch (err) {
@@ -172,7 +204,7 @@ export function createHistoryActions(
if (fallbackMessages.length > 0) {
applyLoadedMessages(fallbackMessages, null);
} else {
set({ messages: [], loading: false });
applyLoadFailure(String(err));
}
}
},