From be03aebb0943fd205a9e58b461ca0bf446686858 Mon Sep 17 00:00:00 2001 From: gaoziman <2942894660@qq.com> Date: Mon, 22 Dec 2025 22:01:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=A1=B5=E9=9D=A2):=20=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E9=9B=86=E6=88=90=E8=A7=86=E9=A2=91=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=92=8C=E9=93=BE=E6=8E=A5=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MessageBubble.tsx 消息气泡组件: - 新增 searchVideos 属性接收视频搜索结果 - 新增 onLinkClick 属性支持链接点击回调 - 在消息内容中渲染 SearchVideosGrid 组件 - 将 onLinkClick 传递给 MarkdownRenderer - chat/[id]/page.tsx 聊天页面: - 添加 LinkPreviewModal 链接预览弹窗状态管理 - 新增 handleLinkClick 处理链接点击打开预览 - MessageBubble 传递 searchVideos 和 onLinkClick - 渲染 LinkPreviewModal 组件 --- src/app/chat/[id]/page.tsx | 20 ++++++++++++++++++++ src/components/features/MessageBubble.tsx | 13 ++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index ca8d10a..6c87f07 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -10,6 +10,7 @@ import { MessageBubble } from '@/components/features/MessageBubble'; import { ChatHeaderInfo } from '@/components/features/ChatHeader'; import { SaveToNoteModal } from '@/components/features/SaveToNoteModal'; import { PromptOptimizer } from '@/components/features/PromptOptimizer'; +import { LinkPreviewModal } from '@/components/features/LinkPreviewModal'; import { cn } from '@/lib/utils'; import { useConversation, useConversations } from '@/hooks/useConversations'; import { useStreamChat, type ChatMessage } from '@/hooks/useStreamChat'; @@ -47,6 +48,10 @@ export default function ChatPage({ params }: PageProps) { const [noteModalOpen, setNoteModalOpen] = useState(false); const [noteContent, setNoteContent] = useState(''); + // 链接预览状态 + const [linkPreviewOpen, setLinkPreviewOpen] = useState(false); + const [linkPreviewUrl, setLinkPreviewUrl] = useState(null); + // 获取数据 const { conversation, loading: conversationLoading, error: conversationError } = useConversation(chatId); const { createConversation, updateConversation, deleteConversation } = useConversations(); @@ -342,6 +347,12 @@ export default function ChatPage({ params }: PageProps) { } }; + // 处理链接点击 - 在预览弹窗中打开 + const handleLinkClick = (url: string) => { + setLinkPreviewUrl(url); + setLinkPreviewOpen(true); + }; + // 转换模型格式 const modelOptions = models.map((m) => ({ id: m.modelId, @@ -568,12 +579,14 @@ export default function ChatPage({ params }: PageProps) { error={message.error} images={message.images} searchImages={message.searchImages} + searchVideos={message.searchVideos} uploadedImages={message.uploadedImages} uploadedDocuments={message.uploadedDocuments} usedTools={message.usedTools} pyodideStatus={message.pyodideStatus} onRegenerate={message.role === 'assistant' && !isStreaming ? handleRegenerate : undefined} onSaveToNote={message.role === 'assistant' && !isStreaming ? handleSaveToNote : undefined} + onLinkClick={handleLinkClick} conversationId={chatId} /> )) @@ -624,6 +637,13 @@ export default function ChatPage({ params }: PageProps) { conversationId={chatId} /> + {/* 链接预览弹窗 */} + setLinkPreviewOpen(false)} + /> + {/* 提示词优化工具浮动按钮 */} diff --git a/src/components/features/MessageBubble.tsx b/src/components/features/MessageBubble.tsx index 021fe33..d7a0349 100644 --- a/src/components/features/MessageBubble.tsx +++ b/src/components/features/MessageBubble.tsx @@ -8,6 +8,7 @@ import { Tooltip } from '@/components/ui/Tooltip'; import { MarkdownRenderer } from '@/components/markdown/MarkdownRenderer'; import { CodeExecutionResult, PyodideLoading } from '@/components/features/CodeExecutionResult'; import { SearchImagesGrid, type SearchImageItem } from '@/components/features/SearchImagesGrid'; +import { SearchVideosGrid, type SearchVideoItem } from '@/components/features/SearchVideosGrid'; import { ImageLightbox } from '@/components/ui/ImageLightbox'; import { DocumentPreview, type DocumentData } from '@/components/ui/DocumentPreview'; import { cn } from '@/lib/utils'; @@ -33,6 +34,8 @@ interface MessageBubbleProps { images?: string[]; /** 搜索到的图片(来自图片搜索工具) */ searchImages?: SearchImageItem[]; + /** 搜索到的视频(来自视频搜索工具) */ + searchVideos?: SearchVideoItem[]; /** 用户上传的图片(Base64 或 URL) */ uploadedImages?: string[]; /** 用户上传的文档 */ @@ -49,6 +52,8 @@ interface MessageBubbleProps { onRegenerate?: (messageId: string) => void; /** 保存到笔记回调(仅对 AI 消息有效),传入消息内容 */ onSaveToNote?: (content: string) => void; + /** 链接点击回调,用于在预览窗口中打开链接 */ + onLinkClick?: (url: string) => void; /** 对话 ID(用于关联笔记来源) */ conversationId?: string; } @@ -70,7 +75,7 @@ function getDocumentIcon(type: string) { return FileText; } -export function MessageBubble({ message, user, thinkingContent, isStreaming, error, images, searchImages, uploadedImages, uploadedDocuments, usedTools, pyodideStatus, onRegenerate, onSaveToNote, conversationId }: MessageBubbleProps) { +export function MessageBubble({ message, user, thinkingContent, isStreaming, error, images, searchImages, searchVideos, uploadedImages, uploadedDocuments, usedTools, pyodideStatus, onRegenerate, onSaveToNote, onLinkClick, conversationId }: MessageBubbleProps) { const isUser = message.role === 'user'; const [thinkingExpanded, setThinkingExpanded] = useState(false); const [copied, setCopied] = useState(false); @@ -284,11 +289,17 @@ export function MessageBubble({ message, user, thinkingContent, isStreaming, err )} + {/* 搜索到的视频(视频搜索工具结果)- 显示在图片下方 */} + {searchVideos && searchVideos.length > 0 && ( + + )} +
{message.content ? ( ) : isStreaming ? (