import { type AnnotationQueueItem, type ScoreConfigDomain, } from "@langfuse/shared"; import { AnnotationDrawerSection } from "../shared/AnnotationDrawerSection"; import { AnnotationProcessingLayout } from "../shared/AnnotationProcessingLayout"; import { SessionIO } from "@/src/components/session"; import { useState, useEffect } from "react"; import { Button } from "@/src/components/ui/button"; import { ItemBadge } from "@/src/components/ItemBadge"; import { CopyIdsPopover } from "@/src/components/trace2/components/_shared/CopyIdsPopover"; import { Badge } from "@/src/components/ui/badge"; import { Separator } from "@/src/components/ui/separator"; import Link from "next/link"; import { Card } from "@/src/components/ui/card"; interface SessionAnnotationProcessorProps { item: AnnotationQueueItem & { parentTraceId?: string | null; lockedByUser: { name: string | null | undefined } | null; }; data: any; // // Session data with scores configs: ScoreConfigDomain[]; projectId: string; } // some projects have thousands of traces in a session, paginate to avoid rendering all at once const PAGE_SIZE = 10; export const SessionAnnotationProcessor: React.FC< SessionAnnotationProcessorProps > = ({ item, data, configs, projectId }) => { const [visibleTraces, setVisibleTraces] = useState(PAGE_SIZE); const [currentTraceIndex, setCurrentTraceIndex] = useState(1); // Intersection observer to which trace is currently in view useEffect(() => { if (!data?.traces) return; const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting && entry.intersectionRatio > 0.5) { const index = parseInt( entry.target.getAttribute("data-trace-index") || "0", ); setCurrentTraceIndex(index + 1); } }); }, { threshold: 0.5, // Trigger when 50% of trace is visible rootMargin: "-25% 0px -25% 0px", // Focus on center area }, ); // Observe all trace cards const traceCards = document.querySelectorAll("[data-trace-index]"); traceCards.forEach((card) => observer.observe(card)); return () => observer.disconnect(); }, [data?.traces, visibleTraces]); const leftPanel = (
{/* Sticky Header */}
{item.objectId}
{data?.traces && (
Trace {currentTraceIndex} / {data.traces.length}
)}
{data.environment && ( Env: {data.environment} )} Total traces: {data?.traces.length}
{/* Scrollable Content */}
{data?.traces .slice(0, visibleTraces) .map((trace: any, index: number) => (
Trace: {trace.name} ({trace.id}) ↗
{trace.timestamp.toLocaleString()}
))} {data?.traces && data.traces.length > visibleTraces && (
)}
); const rightPanel = ( ); return ( ); };