diff --git a/apps/desktop/src/components/EnhancedChatMessageV2.tsx b/apps/desktop/src/components/EnhancedChatMessageV2.tsx index fdbe8a3..faf1a24 100644 --- a/apps/desktop/src/components/EnhancedChatMessageV2.tsx +++ b/apps/desktop/src/components/EnhancedChatMessageV2.tsx @@ -7,12 +7,9 @@ import { Clock, ChevronDown, ChevronUp, - ExternalLink, - Grid3X3, - List } from 'lucide-react'; import { EnhancedMarkdownRenderer } from './EnhancedMarkdownRenderer'; -import { ChatMaterialCard } from './ChatMaterialCard'; +import ImageCard from './ImageCard'; import { GroundingSource } from '../types/ragGrounding'; /** @@ -65,8 +62,6 @@ export const EnhancedChatMessageV2: React.FC = ({ }) => { const [copied, setCopied] = useState(false); const [expandedSources, setExpandedSources] = useState(false); - const [materialViewMode, setMaterialViewMode] = useState<'grid' | 'list'>('grid'); - const [selectedReference, setSelectedReference] = useState(null); const isUser = message.type === 'user'; const isAssistant = message.type === 'assistant'; @@ -84,11 +79,7 @@ export const EnhancedChatMessageV2: React.FC = ({ } }, [message.content]); - // 处理引用点击 - const handleReferenceClick = useCallback((sources: GroundingSource[], index: number) => { - setSelectedReference(selectedReference === index ? null : index); - console.log('引用点击:', { index, sources }); - }, [selectedReference]); + // 处理素材卡片点击 const handleMaterialClick = useCallback((source: GroundingSource) => { @@ -138,7 +129,6 @@ export const EnhancedChatMessageV2: React.FC = ({ groundingMetadata={groundingMetadata} enableReferences={enableReferences} enableMarkdown={true} - onReferenceClick={handleReferenceClick} /> ) : (
@@ -204,41 +194,17 @@ export const EnhancedChatMessageV2: React.FC = ({ )}
- - {expandedSources && ( -
- - -
- )} {expandedSources && ( -
+
{sources.map((source, index) => ( - handleMaterialClick(source as GroundingSource)} /> ))}
diff --git a/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx b/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx index ce583a3..d77da3b 100644 --- a/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx +++ b/apps/desktop/src/components/EnhancedMarkdownRenderer.tsx @@ -37,7 +37,6 @@ interface EnhancedMarkdownRendererProps { export const EnhancedMarkdownRenderer: React.FC = ({ content, groundingMetadata, - enableReferences = true, enableMarkdown = true, className = '', showStatistics = false, @@ -47,8 +46,7 @@ export const EnhancedMarkdownRenderer: React.FC = const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [validation, setValidation] = useState(null); - const [selectedGrounding, setSelectedGrounding] = useState(null); - const [showGroundingModal, setShowGroundingModal] = useState(false); + const [expandedGrounding, setExpandedGrounding] = useState(null); const [previewSource, setPreviewSource] = useState(null); // 解析Markdown内容 @@ -98,17 +96,16 @@ export const EnhancedMarkdownRenderer: React.FC = } }, [parseContent, enableRealTimeParsing]); - // 显示grounding详情 - const showGroundingDetails = useCallback((groundingAnalysis: any) => { - setSelectedGrounding(groundingAnalysis); - setShowGroundingModal(true); - }, []); - - // 关闭grounding详情 - const closeGroundingDetails = useCallback(() => { - setShowGroundingModal(false); - setSelectedGrounding(null); - }, []); + // 切换grounding详情展开状态 + const toggleGroundingDetails = useCallback((groundingAnalysis: any) => { + if (expandedGrounding && expandedGrounding === groundingAnalysis) { + // 如果当前已展开相同的内容,则收起 + setExpandedGrounding(null); + } else { + // 展开新的内容 + setExpandedGrounding(groundingAnalysis); + } + }, [expandedGrounding]); // 查看大图 const handleViewLarge = useCallback((source: any) => { @@ -216,22 +213,36 @@ export const EnhancedMarkdownRenderer: React.FC = }, [groundingMetadata, parseResult]); // 渲染单个Markdown节点 - const renderNode = useCallback((node: MarkdownNode, depth: number = 0, index: number = 0): React.ReactNode => { + const renderNode = useCallback((node: MarkdownNode, depth: number = 0, index: number = 0, showGroundingIndicator: boolean = true): React.ReactNode => { const key = `${node.range?.start?.line || 0}-${node.range?.start?.column || 0}-${node.range?.start?.offset || 0}-${depth}-${index}`; // 分析当前节点的引用关联 const groundingAnalysis = analyzeNodeGrounding(node); // 创建引用指示器组件 - const GroundingIndicator = groundingAnalysis ? ( + const isExpanded = expandedGrounding === groundingAnalysis; + const GroundingIndicator = (groundingAnalysis && showGroundingIndicator) ? ( { console.log('📚 点击查看引用详情:', groundingAnalysis); - // 显示详细的引用信息,包括图片 - showGroundingDetails(groundingAnalysis); + // 切换详细的引用信息展示 + toggleGroundingDetails(groundingAnalysis); }} > - {groundingAnalysis.groundingInfo.sourceCount} + 🖼️ + {groundingAnalysis.groundingInfo.sourceCount} + + + ) : null; @@ -332,7 +343,7 @@ export const EnhancedMarkdownRenderer: React.FC = case MarkdownNodeType.Strong: return ( - {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex))} + {node.children.map((child, childIndex) => renderNode(child, depth + 1, childIndex, false))} ); @@ -424,78 +435,46 @@ export const EnhancedMarkdownRenderer: React.FC =
)} - - {/* 显示引用来源信息(如果有的话) */} - {enableReferences && groundingMetadata?.sources && groundingMetadata.sources.length > 0 && ( -
-
- 基于 {groundingMetadata.sources.length} 个来源的信息 + {/* Grounding详情内联展开 */} + {expandedGrounding && ( +
+
+

+ 🖼️ + 相关素材详情 ({expandedGrounding.groundingInfo?.sourceCount || 0}) +

+
-
- {groundingMetadata.sources.slice(0, 5).map((source, index) => ( - showGroundingDetails({ - groundingInfo: { - sourceCount: 1, - sources: [source] - } - })} - > - {index + 1} - - ))} - {groundingMetadata.sources.length > 5 && ( - - +{groundingMetadata.sources.length - 5} 更多 - - )} -
-
- )} - {/* Grounding详情模态框 */} - {showGroundingModal && selectedGrounding && ( -
-
- {/* 模态框头部 */} -
-

相关素材详情

- -
- - {/* 模态框内容 */} -
-
- {selectedGrounding.groundingInfo?.sources?.map((source: any, index: number) => ( -
+
+ {expandedGrounding.groundingInfo?.sources && expandedGrounding.groundingInfo.sources.length > 0 ? ( +
+ {expandedGrounding.groundingInfo.sources.map((source: any, index: number) => ( +
))}
- - {/* 如果没有图片,显示提示信息 */} - {(!selectedGrounding.groundingInfo?.sources || selectedGrounding.groundingInfo.sources.length === 0) && ( -
-
🖼️
-

暂无相关图片素材

-
- )} -
+ ) : ( +
+
🖼️
+

暂无相关图片素材

+

该内容段落没有关联的图片资源

+
+ )}
)} diff --git a/apps/desktop/src/components/ImageCard.tsx b/apps/desktop/src/components/ImageCard.tsx index 70df08c..048225b 100644 --- a/apps/desktop/src/components/ImageCard.tsx +++ b/apps/desktop/src/components/ImageCard.tsx @@ -2,13 +2,11 @@ import React, { useState, useCallback } from 'react'; import { Download, ExternalLink, - Eye, X, Calendar, MapPin, User, Shirt, - Image as ImageIcon, AlertCircle } from 'lucide-react'; import { GroundingSource } from '../types/ragGrounding'; @@ -92,13 +90,6 @@ export const ImageCard: React.FC = ({ } }, [onDownload, source, isDownloading]); - // 处理查看大图 - const handleViewLarge = useCallback(() => { - if (onViewLarge) { - onViewLarge(source); - } - }, [onViewLarge, source]); - // 构建位置样式 const positionStyle = position ? { position: 'absolute' as const, @@ -111,58 +102,9 @@ export const ImageCard: React.FC = ({ return (
- {/* 卡片头部 */} -
-
- -

{title}

-
-
- {onViewLarge && ( - - )} - {onDownload && ( - - )} - {imageUri && ( - - - - )} - {onClose && ( - - )} -
-
- {/* 图片展示区域 */}
{imageUri && !imageError ? ( @@ -170,20 +112,56 @@ export const ImageCard: React.FC = ({ {description + + {/* 悬浮按钮组 */} +
+ {onDownload && ( + + )} + {imageUri && ( + + + + )} + {onClose && ( + + )} +
+ + {/* 加载指示器 */} {!imageLoaded && ( -
+
)}
) : ( -
+

图片无法加载

@@ -194,71 +172,77 @@ export const ImageCard: React.FC = ({ {/* 详细信息区域 */} {showDetails && ( -
+
{/* 描述 */} {description && ( -

{description}

+

{description}

)} - {/* 环境标签 */} - {environmentTags.length > 0 && ( -
- -
- {environmentTags.slice(0, 3).map((tag: string, index: number) => ( - - {tag} - - ))} - {environmentTags.length > 3 && ( - +{environmentTags.length - 3} - )} + {/* 标签和信息 */} +
+ {/* 环境标签 */} + {environmentTags.length > 0 && ( +
+ +
+ {environmentTags.slice(0, 2).map((tag: string, index: number) => ( + + {tag} + + ))} + {environmentTags.length > 2 && ( + +{environmentTags.length - 2} + )} +
-
- )} + )} - {/* 服装类别 */} - {categories.length > 0 && ( -
- -
- {categories.slice(0, 4).map((category: string, index: number) => ( - - {category} - - ))} - {categories.length > 4 && ( - +{categories.length - 4} - )} + {/* 服装类别 */} + {categories.length > 0 && ( +
+ +
+ {categories.slice(0, 2).map((category: string, index: number) => ( + + {category} + + ))} + {categories.length > 2 && ( + +{categories.length - 2} + )} +
-
- )} + )} - {/* 模特信息 */} - {models.length > 0 && ( -
- - - {models.length} 位模特 - -
- )} + {/* 底部信息行 */} +
+ {/* 模特信息 */} + {models.length > 0 && ( +
+ + + {models.length} 位模特 + +
+ )} - {/* 发布时间 */} - {releaseDate && ( -
- - - {new Date(releaseDate).toLocaleDateString('zh-CN')} - + {/* 发布时间 */} + {releaseDate && ( +
+ + + {new Date(releaseDate).toLocaleDateString('zh-CN')} + +
+ )}
- )} +
)}