"use client"; import { useQuery } from "@tanstack/react-query"; import { ChevronDown, ChevronUp, ExternalLink, Loader2 } from "lucide-react"; import type React from "react"; import { type ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react"; import { MarkdownViewer } from "@/components/markdown-viewer"; import { Button } from "@/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from "@/components/ui/sheet"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { documentsApiService } from "@/lib/apis/documents-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { cn } from "@/lib/utils"; interface SourceDetailSheetProps { open: boolean; onOpenChange: (open: boolean) => void; chunkId: number; sourceType: string; title: string; description?: string; url?: string; children?: ReactNode; } const formatDocumentType = (type: string) => { return type .split("_") .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) .join(" "); }; export function SourceDetailSheet({ open, onOpenChange, chunkId, sourceType, title, description, url, children, }: SourceDetailSheetProps) { const chunksContainerRef = useRef(null); const highlightedChunkRef = useRef(null); const [summaryOpen, setSummaryOpen] = useState(false); // Add useQuery to fetch document by chunk const { data: document, isLoading: isDocumentByChunkFetching, error: documentByChunkFetchingError, } = useQuery({ queryKey: cacheKeys.documents.byChunk(chunkId.toString()), queryFn: () => documentsApiService.getDocumentByChunk({ chunk_id: chunkId }), enabled: !!chunkId && open, staleTime: 5 * 60 * 1000, // 5 minutes }); // Check if this is a source type that should render directly from node const isDirectRenderSource = sourceType === "TAVILY_API" || sourceType === "LINKUP_API" || sourceType === "SEARXNG_API" || sourceType === "BAIDU_SEARCH_API"; useEffect(() => { // Scroll to highlighted chunk when document loads if (document) { setTimeout(() => { highlightedChunkRef.current?.scrollIntoView({ behavior: "smooth", block: "start", }); }, 100); } }, [document, open]); const handleUrlClick = (e: React.MouseEvent, clickUrl: string) => { e.preventDefault(); e.stopPropagation(); window.open(clickUrl, "_blank", "noopener,noreferrer"); }; return ( {children} {getConnectorIcon(sourceType)} {document?.title || title} {document ? formatDocumentType(document.document_type) : sourceType && formatDocumentType(sourceType)} {!isDirectRenderSource && isDocumentByChunkFetching && (
)} {!isDirectRenderSource && documentByChunkFetchingError && (

{documentByChunkFetchingError.message || "Failed to load document"}

)} {/* Direct render for web search providers */} {isDirectRenderSource && (
{/* External Link */} {url && (
)} {/* Source Information */}

Source Information

{title || "Untitled"}
{description || "No content available"}
)} {/* API-fetched document content */} {!isDirectRenderSource && document && (
{/* Document Metadata */} {document.document_metadata && Object.keys(document.document_metadata).length > 0 && (

Document Information

{Object.entries(document.document_metadata).map(([key, value]) => (
{key.replace(/_/g, " ")}:
{String(value)}
))}
)} {/* External Link */} {url && (
)} {/* Chunks */}
{/* Header row: header and button side by side */}

Document Content

{document.content && ( Summary {summaryOpen ? ( ) : ( )} )}
{/* Expanded summary content: always full width, below the row */} {document.content && (
)}
{document.chunks.map((chunk, idx) => (
Chunk {idx + 1} of {document.chunks.length} {chunk.id === chunkId && ( Referenced Chunk )}
))}
)}
); }