fix: 修复 AI 消息渲染顺序错乱和搜索结果卡片重复问题

有 tool_calls 的中间 AI 消息不再渲染独立 MessageBubble 正文气泡,
thinking 内容移至 ToolCallStatus 上方作为步骤说明,
最终 AI 消息(无 tool_calls)才渲染 MessageBubble,确保工具步骤在前、正文回答在后。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gongzhiyong
2026-04-13 15:44:36 +08:00
co-authored by Claude Sonnet 4.6
parent 7d035b21b7
commit bfcf22d4f8
+22 -13
View File
@@ -913,24 +913,19 @@ function App() {
const toolCalls: { name?: string; id?: string }[] = (message as any).tool_calls ?? [];
const isLastAi = idx === lastAiIdx;
const hasToolCalls = toolCalls.length > 0;
return (
<div key={message.id ?? idx} className="flex flex-col gap-3">
{/* Text reply — rendered first so user sees conclusion before evidence */}
{(plainTextContent || hasThinking) && (
<div className="group relative max-w-[85%] rounded-2xl rounded-bl-sm bg-muted text-foreground px-4 py-2.5 text-sm">
<MessageBubble content={bubbleContent} role="ai" isStreaming={thread.isLoading && isLastAi} />
{thread.isLoading && isLastAi && (
<span className="typing-cursor" aria-hidden="true" />
)}
{/* Copy button */}
<div className="flex justify-end mt-1">
<CopyButton text={plainTextContent} />
</div>
{/* Tool call status (with inline expand/collapse for UI cards) */}
{/* Thinking block for intermediate messages (with tool_calls): shown above tool status */}
{hasThinking && hasToolCalls && (
<div className="max-w-[85%]">
<MessageBubble content={bubbleContent} role="ai" isStreaming={false} />
</div>
)}
{/* Tool call status (with inline expand/collapse for UI cards) */}
{toolCalls.length > 0 && (
{hasToolCalls && (
<ToolCallStatus
toolCalls={toolCalls}
isLoading={thread.isLoading}
@@ -943,6 +938,20 @@ function App() {
/>
)}
{/* Text reply — only for final AI messages without tool_calls */}
{(plainTextContent || hasThinking) && !hasToolCalls && (
<div className="group relative max-w-[85%] rounded-2xl rounded-bl-sm bg-muted text-foreground px-4 py-2.5 text-sm">
<MessageBubble content={bubbleContent} role="ai" isStreaming={thread.isLoading && isLastAi} />
{thread.isLoading && isLastAi && (
<span className="typing-cursor" aria-hidden="true" />
)}
{/* Copy button */}
<div className="flex justify-end mt-1">
<CopyButton text={plainTextContent} />
</div>
</div>
)}
{/* UI cards not matched to any tool call (standalone) */}
{(() => {
const standaloneUiItems = uiItems.filter((ui) => !toolCalls.some((tc) => {