From e5c5593686dc236541bed1d97294043a9ea774b7 Mon Sep 17 00:00:00 2001 From: gaoziman <2942894660@qq.com> Date: Fri, 19 Dec 2025 20:20:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=BB=84=E4=BB=B6):=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=B0=94=E6=B3=A1=E6=94=AF=E6=8C=81=E5=9B=BE=E5=BD=A2=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MessageBubble.tsx: - 集成 CodeExecutionResult 组件显示代码执行图片 - 添加 Pyodide 加载状态显示 - 支持 images 和 pyodideStatus 属性 - 新增 ToolResultDisplay 子组件处理工具结果 MarkdownRenderer.tsx: - 修复图片组件属性传递问题 - 改用 spread 操作符传递所有 img 属性 --- src/components/features/MessageBubble.tsx | 70 +++++++++++++++++++- src/components/markdown/MarkdownRenderer.tsx | 6 +- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/components/features/MessageBubble.tsx b/src/components/features/MessageBubble.tsx index 9d922d5..56160cb 100644 --- a/src/components/features/MessageBubble.tsx +++ b/src/components/features/MessageBubble.tsx @@ -5,8 +5,9 @@ import { Copy, ThumbsUp, ThumbsDown, RefreshCw, ChevronDown, ChevronUp, Brain, L import { Avatar } from '@/components/ui/Avatar'; import { AILogo } from '@/components/ui/AILogo'; import { MarkdownRenderer } from '@/components/markdown/MarkdownRenderer'; +import { CodeExecutionResult, PyodideLoading } from '@/components/features/CodeExecutionResult'; import { cn } from '@/lib/utils'; -import type { Message, User } from '@/types'; +import type { Message, User, ToolResult } from '@/types'; interface MessageBubbleProps { message: Message; @@ -14,9 +15,17 @@ interface MessageBubbleProps { thinkingContent?: string; isStreaming?: boolean; error?: string; + /** 代码执行产生的图片(Base64) */ + images?: string[]; + /** Pyodide 加载状态 */ + pyodideStatus?: { + stage: 'loading' | 'ready' | 'error'; + message: string; + progress?: number; + }; } -export function MessageBubble({ message, user, thinkingContent, isStreaming, error }: MessageBubbleProps) { +export function MessageBubble({ message, user, thinkingContent, isStreaming, error, images, pyodideStatus }: MessageBubbleProps) { const isUser = message.role === 'user'; const [thinkingExpanded, setThinkingExpanded] = useState(false); const [copied, setCopied] = useState(false); @@ -96,6 +105,34 @@ export function MessageBubble({ message, user, thinkingContent, isStreaming, err ) : null} + {/* 工具调用结果 - 代码执行图片展示 */} + {message.toolResults && message.toolResults.length > 0 && ( +
+ {message.toolResults.map((result, index) => ( + + ))} +
+ )} + + {/* Pyodide 加载状态 */} + {pyodideStatus && ( +
+ +
+ )} + + {/* 代码执行图片(从 props 传入) */} + {images && images.length > 0 && ( + + )} + {/* 流式状态指示器 */} {isStreaming && message.content && (
@@ -149,3 +186,32 @@ function ActionButton({ icon: Icon, title, onClick }: ActionButtonProps) { ); } + +/** + * 工具调用结果显示组件 + * 专门处理代码执行结果和图片显示 + */ +interface ToolResultDisplayProps { + result: ToolResult; +} + +function ToolResultDisplay({ result }: ToolResultDisplayProps) { + // 只有代码执行工具才显示图片 + if (result.toolName !== 'code_execution') { + return null; + } + + // 如果没有图片,不显示 + if (!result.images || result.images.length === 0) { + return null; + } + + return ( + + ); +} diff --git a/src/components/markdown/MarkdownRenderer.tsx b/src/components/markdown/MarkdownRenderer.tsx index 6d12969..c5e24f7 100644 --- a/src/components/markdown/MarkdownRenderer.tsx +++ b/src/components/markdown/MarkdownRenderer.tsx @@ -196,11 +196,11 @@ const markdownComponents = { }, // 图片 - img({ src, alt }: { src?: string; alt?: string }) { + img(props: React.ImgHTMLAttributes) { return ( {alt );