fix(desktop): prevent empty blocks rendering in chat UI

- Guard empty thinking events from creating blank ThinkingBlock rows
- Skip empty assistant_text from history loading
- Hide ToolResultBlock when content is empty (non-error)
- Add component-level null returns as safety net

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 13:11:37 +08:00
co-authored by Claude Opus 4.6
parent 838f3717e5
commit 20ca3421ab
4 changed files with 12 additions and 1 deletions
@@ -8,6 +8,8 @@ type Props = {
} }
export function AssistantMessage({ content, isStreaming }: Props) { export function AssistantMessage({ content, isStreaming }: Props) {
if (!content.trim() && !isStreaming) return null
const documentLayout = shouldUseDocumentLayout(content) const documentLayout = shouldUseDocumentLayout(content)
return ( return (
@@ -17,6 +17,8 @@ export function ThinkingBlock({ content, isActive = false }: { content: string;
const firstLine = lines[0]?.replace(/\s+/g, ' ').trim() || '' const firstLine = lines[0]?.replace(/\s+/g, ' ').trim() || ''
const preview = firstLine.length > 80 ? firstLine.slice(0, 80) + '...' : firstLine const preview = firstLine.length > 80 ? firstLine.slice(0, 80) + '...' : firstLine
if (!content.trim() && !isActive) return null
return ( return (
<div className="mb-1"> <div className="mb-1">
<style>{thinkingStyles}</style> <style>{thinkingStyles}</style>
@@ -23,6 +23,8 @@ export function ToolResultBlock({ content, isError, toolName, standalone = true
if (!standalone) return null if (!standalone) return null
const text = extractText(content) const text = extractText(content)
if (!text.trim() && !isError) return null
const preview = text.slice(0, 200) const preview = text.slice(0, 200)
const hasMore = text.length > 200 const hasMore = text.length > 200
+6 -1
View File
@@ -634,6 +634,9 @@ export const useChatStore = create<ChatStore>((set, get) => ({
updated[updated.length - 1] = { ...last, content: last.content + msg.text } updated[updated.length - 1] = { ...last, content: last.content + msg.text }
return { messages: updated, chatState: 'thinking', activeThinkingId: last.id, streamingText: '' } return { messages: updated, chatState: 'thinking', activeThinkingId: last.id, streamingText: '' }
} }
if (!msg.text) {
return { chatState: 'thinking', streamingText: '' }
}
const id = nextId() const id = nextId()
return { return {
messages: [...base, { id, type: 'thinking', content: msg.text, timestamp: Date.now() }], messages: [...base, { id, type: 'thinking', content: msg.text, timestamp: Date.now() }],
@@ -1037,7 +1040,9 @@ export function mapHistoryMessagesToUiMessages(
continue continue
} }
if (msg.type === 'assistant' && typeof msg.content === 'string') { if (msg.type === 'assistant' && typeof msg.content === 'string') {
uiMessages.push({ id: msg.id || nextId(), type: 'assistant_text', content: msg.content, timestamp, model: msg.model }) if (msg.content.trim()) {
uiMessages.push({ id: msg.id || nextId(), type: 'assistant_text', content: msg.content, timestamp, model: msg.model })
}
continue continue
} }
if ((msg.type === 'assistant' || msg.type === 'tool_use') && Array.isArray(msg.content)) { if ((msg.type === 'assistant' || msg.type === 'tool_use') && Array.isArray(msg.content)) {