fix(desktop): keep image-only messages in turn order #1095

This commit is contained in:
程序员阿江(Relakkes) 2026-07-28 22:09:48 +08:00
parent d141543794
commit eef49d3395
2 changed files with 63 additions and 1 deletions

View File

@ -5559,6 +5559,56 @@ describe('chatStore history mapping', () => {
expect(userMessages).toHaveLength(1)
})
it('keeps an image-only replay before the assistant response', () => {
useChatStore.setState({
sessions: {
[TEST_SESSION_ID]: makeSession({
messages: [
{
id: 'live-user',
type: 'user_text',
content: '',
attachments: [{
type: 'image',
name: 'screenshot.png',
data: 'data:image/png;base64,AAAA',
mimeType: 'image/png',
}],
timestamp: 1,
},
{
id: 'assistant-answer',
type: 'assistant_text',
content: 'The screenshot shows a repository list.',
timestamp: 2,
},
],
chatState: 'thinking',
}),
},
})
useChatStore.getState().handleServerMessage(TEST_SESSION_ID, {
type: 'user_message_replay',
content: [
'Please analyze the attached image.',
'[Image source: /Users/me/.claude/uploads/session/screenshot.png]',
].join('\n'),
})
expect(useChatStore.getState().sessions[TEST_SESSION_ID]?.messages).toMatchObject([
{
type: 'user_text',
content: '',
attachments: [{ type: 'image', name: 'screenshot.png' }],
},
{
type: 'assistant_text',
content: 'The screenshot shows a repository list.',
},
])
})
it('dedupes a path-backed image after the server inlines it into replay content', () => {
const prompt = '检查这张截图'
const imagePath = 'C:\\Users\\tester\\Desktop\\screen.png'

View File

@ -3749,6 +3749,7 @@ function extractRestoredUserDisplay(text: string): RestoredUserDisplay {
// ConversationService stores data-only files as `${randomUUID()}-${sanitizedName}`
// and omits successfully inlined images from the replayed text content.
const MATERIALIZED_UPLOAD_NAME_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}-(.+)$/i
const IMAGE_ONLY_REPLAY_FALLBACK = 'Please analyze the attached image.'
function isLikelyInlineImageAttachment(attachment: UIAttachment): boolean {
if (attachment.type === 'image') return true
@ -3800,12 +3801,23 @@ function replayMatchesCurrentUserMessage(
const currentModelContent = (message.modelContent ?? message.content).trim()
if (currentModelContent === replayModelContent) return true
const currentAttachments = message.attachments ?? []
if (
message.content.trim() === '' &&
replayDisplay.content.trim() === IMAGE_ONLY_REPLAY_FALLBACK &&
!replayDisplay.attachments?.length &&
currentAttachments.length > 0 &&
currentAttachments.every(isLikelyInlineImageAttachment)
) {
return true
}
const currentDisplay = extractRestoredUserDisplay(currentModelContent)
if (currentDisplay.content.trim() !== replayDisplay.content.trim()) return false
return replayAttachmentsMatchCurrent(
replayDisplay.attachments ?? [],
message.attachments ?? [],
currentAttachments,
)
}