// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { ExternalLink, Globe, Clock, Star } from "lucide-react";
import { useMemo } from "react";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "~/components/ui/hover-card";
import { cn } from "~/lib/utils";
import type { Citation } from "~/core/messages";
// Re-export Citation type as CitationData for backward compatibility
export type CitationData = Citation;
interface CitationLinkProps {
href: string;
children: React.ReactNode;
citations: CitationData[];
className?: string;
id?: string;
}
/**
* Enhanced link component that shows citation metadata on hover.
* Used within markdown content to provide rich citation information.
*/
export function CitationLink({
href,
children,
citations,
className,
id,
}: CitationLinkProps) {
// Find matching citation data for this URL
const { citation, index } = useMemo(() => {
if (!href || !citations) return { citation: null, index: -1 };
// Try exact match first
let matchIndex = citations.findIndex((c) => c.url === href);
// If not found, try versatile comparison using normalized URLs
if (matchIndex === -1) {
const normalizeUrl = (url: string) => {
try {
return decodeURIComponent(url).trim();
} catch {
return url.trim();
}
};
const normalizedHref = normalizeUrl(href);
matchIndex = citations.findIndex(
(c) => normalizeUrl(c.url) === normalizedHref
);
}
const match = matchIndex !== -1 ? citations[matchIndex] : null;
return { citation: match, index: matchIndex };
}, [href, citations]);
// If no citation data found, render as regular link
if (!citation) {
return (
{children}
);
}
const handleCitationClick = (e: React.MouseEvent) => {
// If it's an internal-looking citation (e.g. [1])
// or if the user clicks the citation number in the text
// we try to scroll to the reference list at the bottom
if (index !== -1) {
const targetId = `ref-${index + 1}`;
const element = document.getElementById(targetId);
if (element) {
e.preventDefault();
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}
// If element not found or index is -1, let the default behavior (open URL) happen
};
return (
{citation.domain}
)} {citation.description && ({citation.description}
)}