"use client"; import { Badge } from "@/components/ui/badge"; import { Carousel, type CarouselApi, CarouselContent, CarouselItem, } from "@/components/ui/carousel"; import { HoverCard, HoverCardContent, HoverCardTrigger, } from "@/components/ui/hover-card"; import { cn } from "@/lib/utils"; import { ExternalLinkIcon, ArrowLeftIcon, ArrowRightIcon } from "lucide-react"; import { type ComponentProps, createContext, useCallback, useContext, useEffect, useState, } from "react"; import type { Citation } from "@/core/citations"; import { extractDomainFromUrl } from "@/core/citations"; import { Shimmer } from "./shimmer"; import { useI18n } from "@/core/i18n/hooks"; export type InlineCitationProps = ComponentProps<"span">; export const InlineCitation = ({ className, ...props }: InlineCitationProps) => ( ); export type InlineCitationTextProps = ComponentProps<"span">; export const InlineCitationText = ({ className, ...props }: InlineCitationTextProps) => ( ); export type InlineCitationCardProps = ComponentProps; export const InlineCitationCard = (props: InlineCitationCardProps) => ( ); export type InlineCitationCardTriggerProps = ComponentProps & { sources: string[]; }; export const InlineCitationCardTrigger = ({ sources, className, ...props }: InlineCitationCardTriggerProps) => ( {sources[0] ? ( <> {new URL(sources[0]).hostname}{" "} {sources.length > 1 && `+${sources.length - 1}`} ) : ( "unknown" )} ); export type InlineCitationCardBodyProps = ComponentProps<"div">; export const InlineCitationCardBody = ({ className, ...props }: InlineCitationCardBodyProps) => ( ); const CarouselApiContext = createContext(undefined); const useCarouselApi = () => { const context = useContext(CarouselApiContext); return context; }; export type InlineCitationCarouselProps = ComponentProps; export const InlineCitationCarousel = ({ className, children, ...props }: InlineCitationCarouselProps) => { const [api, setApi] = useState(); return ( {children} ); }; export type InlineCitationCarouselContentProps = ComponentProps<"div">; export const InlineCitationCarouselContent = ( props: InlineCitationCarouselContentProps ) => ; export type InlineCitationCarouselItemProps = ComponentProps<"div">; export const InlineCitationCarouselItem = ({ className, ...props }: InlineCitationCarouselItemProps) => ( ); export type InlineCitationCarouselHeaderProps = ComponentProps<"div">; export const InlineCitationCarouselHeader = ({ className, ...props }: InlineCitationCarouselHeaderProps) => (
); export type InlineCitationCarouselIndexProps = ComponentProps<"div">; export const InlineCitationCarouselIndex = ({ children, className, ...props }: InlineCitationCarouselIndexProps) => { const api = useCarouselApi(); const [current, setCurrent] = useState(0); const [count, setCount] = useState(0); useEffect(() => { if (!api) { return; } setCount(api.scrollSnapList().length); setCurrent(api.selectedScrollSnap() + 1); api.on("select", () => { setCurrent(api.selectedScrollSnap() + 1); }); }, [api]); return (
{children ?? `${current}/${count}`}
); }; export type InlineCitationCarouselPrevProps = ComponentProps<"button">; export const InlineCitationCarouselPrev = ({ className, ...props }: InlineCitationCarouselPrevProps) => { const api = useCarouselApi(); const handleClick = useCallback(() => { if (api) { api.scrollPrev(); } }, [api]); return ( ); }; export type InlineCitationCarouselNextProps = ComponentProps<"button">; export const InlineCitationCarouselNext = ({ className, ...props }: InlineCitationCarouselNextProps) => { const api = useCarouselApi(); const handleClick = useCallback(() => { if (api) { api.scrollNext(); } }, [api]); return ( ); }; export type InlineCitationSourceProps = ComponentProps<"div"> & { title?: string; url?: string; description?: string; }; export const InlineCitationSource = ({ title, url, description, className, children, ...props }: InlineCitationSourceProps) => (
{title && (

{title}

)} {url && (

{url}

)} {description && (

{description}

)} {children}
); export type InlineCitationQuoteProps = ComponentProps<"blockquote">; export const InlineCitationQuote = ({ children, className, ...props }: InlineCitationQuoteProps) => (
{children}
); /** * Shared CitationLink component that renders a citation as a hover card badge * Used across message-list-item, artifact-file-detail, and message-group * * When citation is provided, displays title and snippet from the citation. * When citation is omitted, falls back to displaying the domain name extracted from href. */ export type CitationLinkProps = { citation?: Citation; href: string; children: React.ReactNode; }; export const CitationLink = ({ citation, href, children, }: CitationLinkProps) => { const domain = extractDomainFromUrl(href); return ( e.stopPropagation()} > {children ?? domain} ); }; /** * Shared CitationsLoadingIndicator component * Used across message-list-item and message-group to show loading citations */ export type CitationsLoadingIndicatorProps = { citations: Citation[]; className?: string; }; export const CitationsLoadingIndicator = ({ citations, className, }: CitationsLoadingIndicatorProps) => { const { t } = useI18n(); return (
{citations.length > 0 ? t.citations.loadingCitationsWithCount(citations.length) : t.citations.loadingCitations} {citations.length > 0 && (
{citations.map((citation) => ( {citation.title || extractDomainFromUrl(citation.url)} ))}
)}
); };