'use client';
import { ChevronRight, GitBranch, ArrowRight, Copy, Check } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
import { useState } from 'react';
export interface Hop {
from: string;
rel: string;
to: string;
doc?: string;
}
interface PathViewProps {
hops: Hop[];
conclusion?: string;
alternativePaths?: Hop[][];
hopCount?: number;
entityA?: string;
entityB?: string;
standalone?: boolean;
}
export function PathView({
hops,
conclusion,
alternativePaths = [],
hopCount,
entityA,
entityB,
standalone = false,
}: PathViewProps) {
const [copied, setCopied] = useState(false);
const [activeTab, setActiveTab] = useState(0);
if (!hops || hops.length === 0) return null;
const totalHops = hopCount ?? hops.length;
const handleCopy = () => {
const text = hops
.map((h) => `${h.from} --[${h.rel.replace(/_/g, ' ')}]--> ${h.to}`)
.join('\n');
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const renderChain = (chain: Hop[], index?: number) => (
{index !== undefined && (
Alternative Path {index + 1}
)}
{chain.map((hop, i) => (
{hop.from}
{hop.doc && {hop.doc}}
{hop.rel.replace(/_/g, ' ')}
{i === chain.length - 1 && (
{hop.to}
)}
))}
);
const renderStandalone = () => (
{/* Header */}
Multi-Hop Reasoning Path
{totalHops} hop{totalHops !== 1 ? 's' : ''}
{/* Entity pair header */}
{entityA && entityB && (
)}
{/* Vertical timeline layout */}
{hops.map((hop, i) => (
{/* Timeline line */}
{i + 1}
{i < hops.length - 1 && (
)}
{/* Hop content */}
{hop.from}
{hop.rel.replace(/_/g, ' ')}
{hop.to}
{hop.doc && (
Source: {hop.doc}
)}
))}
{/* Alternative paths tabs */}
{alternativePaths.length > 0 && (
{alternativePaths.map((_, i) => (
))}
{activeTab === 0 && renderChain(hops)}
{activeTab > 0 && renderChain(alternativePaths[activeTab - 1], activeTab)}
)}
{/* Conclusion */}
{conclusion && (
Conclusion:
{conclusion}
)}
);
// Compact mode (default — used in Query page)
return (
Multi-Hop Reasoning Path
{renderChain(hops)}
{alternativePaths.length > 0 &&
alternativePaths.map((p, i) => renderChain(p, i + 1))}
{conclusion && (
Conclusion:
{conclusion}
)}
);
}