"use client"; import { useEffect, useState } from "react"; import { ArrowRight } from "@phosphor-icons/react"; import markdownIt from "markdown-it"; const md = new markdownIt({ html: true, linkify: true, typographer: true, }); import { Context, WebPage, OnlineContext } from "../chatMessage/chatMessage"; import { Card } from "@/components/ui/card"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import DOMPurify from "dompurify"; import { getIconFromFilename } from "@/app/common/iconUtils"; interface NotesContextReferenceData { title: string; content: string; } interface NotesContextReferenceCardProps extends NotesContextReferenceData { showFullContent: boolean; } function NotesContextReferenceCard(props: NotesContextReferenceCardProps) { const snippet = props.showFullContent ? DOMPurify.sanitize(md.render(props.content)) : DOMPurify.sanitize(props.content); const fileIcon = getIconFromFilename( props.title || ".txt", "w-6 h-6 text-muted-foreground inline-flex mr-2", ); const [isHovering, setIsHovering] = useState(false); return ( <> setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} className={`${props.showFullContent ? "w-auto" : "w-[200px]"} overflow-hidden break-words text-balance rounded-lg p-2 bg-muted border-none`} >

{fileIcon} {props.title}

{fileIcon} {props.title}

); } export interface ReferencePanelData { notesReferenceCardData: NotesContextReferenceData[]; onlineReferenceCardData: OnlineReferenceData[]; } interface OnlineReferenceData { title: string; description: string; link: string; } interface OnlineReferenceCardProps extends OnlineReferenceData { showFullContent: boolean; } function GenericOnlineReferenceCard(props: OnlineReferenceCardProps) { const [isHovering, setIsHovering] = useState(false); if (!props.link || props.link.split(" ").length > 1) { return null; } let favicon = `https://www.google.com/s2/favicons?domain=globe`; let domain = "unknown"; try { domain = new URL(props.link).hostname; favicon = `https://www.google.com/s2/favicons?domain=${domain}`; } catch (error) { console.warn(`Error parsing domain from link: ${props.link}`); return null; } const handleMouseEnter = () => { setIsHovering(true); }; const handleMouseLeave = () => { setIsHovering(false); }; return ( <>

{domain}

{props.title}

{props.description}

{domain}

{props.title}

{props.description}

); } export function constructAllReferences(contextData: Context[], onlineData: OnlineContext) { const onlineReferences: OnlineReferenceData[] = []; const contextReferences: NotesContextReferenceData[] = []; if (onlineData) { let localOnlineReferences = []; for (const [key, value] of Object.entries(onlineData)) { if (value.answerBox) { localOnlineReferences.push({ title: value.answerBox.title, description: value.answerBox.answer, link: value.answerBox.source, }); } if (value.knowledgeGraph) { localOnlineReferences.push({ title: value.knowledgeGraph.title, description: value.knowledgeGraph.description, link: value.knowledgeGraph.descriptionLink, }); } if (value.webpages) { // If webpages is of type Array, iterate through it and add each webpage to the localOnlineReferences array if (value.webpages instanceof Array) { let webPageResults = value.webpages.map((webPage) => { return { title: webPage.query, description: webPage.snippet, link: webPage.link, }; }); localOnlineReferences.push(...webPageResults); } else { let singleWebpage = value.webpages as WebPage; // If webpages is an object, add the object to the localOnlineReferences array localOnlineReferences.push({ title: singleWebpage.query, description: singleWebpage.snippet, link: singleWebpage.link, }); } } if (value.organic) { let organicResults = value.organic.map((organicContext) => { return { title: organicContext.title, description: organicContext.snippet, link: organicContext.link, }; }); localOnlineReferences.push(...organicResults); } } onlineReferences.push(...localOnlineReferences); } if (contextData) { let localContextReferences = contextData.map((context) => { if (!context.compiled) { const fileContent = context as unknown as string; const title = fileContent.split("\n")[0]; const content = fileContent.split("\n").slice(1).join("\n"); return { title: title, content: content, }; } return { title: context.file, content: context.compiled, }; }); contextReferences.push(...localContextReferences); } return { notesReferenceCardData: contextReferences, onlineReferenceCardData: onlineReferences, }; } export interface TeaserReferenceSectionProps { notesReferenceCardData: NotesContextReferenceData[]; onlineReferenceCardData: OnlineReferenceData[]; isMobileWidth: boolean; } export function TeaserReferencesSection(props: TeaserReferenceSectionProps) { const [numTeaserSlots, setNumTeaserSlots] = useState(3); useEffect(() => { setNumTeaserSlots(props.isMobileWidth ? 1 : 3); }, [props.isMobileWidth]); const notesDataToShow = props.notesReferenceCardData.slice(0, numTeaserSlots); const onlineDataToShow = notesDataToShow.length < numTeaserSlots ? props.onlineReferenceCardData.slice(0, numTeaserSlots - notesDataToShow.length) : []; const shouldShowShowMoreButton = props.notesReferenceCardData.length > 0 || props.onlineReferenceCardData.length > 0; const numReferences = props.notesReferenceCardData.length + props.onlineReferenceCardData.length; if (numReferences === 0) { return null; } return (

References

{numReferences} sources

{notesDataToShow.map((note, index) => { return ( ); })} {onlineDataToShow.map((online, index) => { return ( ); })} {shouldShowShowMoreButton && ( )}
); } interface ReferencePanelDataProps { notesReferenceCardData: NotesContextReferenceData[]; onlineReferenceCardData: OnlineReferenceData[]; } export default function ReferencePanel(props: ReferencePanelDataProps) { if (!props.notesReferenceCardData && !props.onlineReferenceCardData) { return null; } return ( View references References View all references for this response
{props.notesReferenceCardData.map((note, index) => { return ( ); })} {props.onlineReferenceCardData.map((online, index) => { return ( ); })}
); }