diff --git a/backend/danswer/chat/personas.yaml b/backend/danswer/chat/personas.yaml index bafaf2d788c..e628b32e6f7 100644 --- a/backend/danswer/chat/personas.yaml +++ b/backend/danswer/chat/personas.yaml @@ -5,7 +5,7 @@ personas: # this is for DanswerBot to use when tagged in a non-configured channel # Careful setting specific IDs, this won't autoincrement the next ID value for postgres - id: 0 - name: "Knowledge" + name: "Search" description: > Assistant with access to documents from your Connected Sources. # Default Prompt objects attached to the persona, see prompts.yaml diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index 81884105a9c..ca44448297b 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -132,8 +132,9 @@ export function ChatPage({ const { chatSessions, - availableSources, - availableDocumentSets, + ccPairs, + tags, + documentSets, llmProviders, folders, openedFolders, @@ -153,6 +154,12 @@ export function ChatPage({ router.push("/search"); } + const [documentSidebarToggled, setDocumentSidebarToggled] = useState(false); + + const toggleDocumentSidebar = () => { + setDocumentSidebarToggled((prev) => !prev); + }; + const { assistants: availableAssistants, finalAssistants } = useAssistants(); const [showApiKeyModal, setShowApiKeyModal] = useState( @@ -268,6 +275,16 @@ export function ChatPage({ const noAssistants = liveAssistant == null || liveAssistant == undefined; + const availableSources = ccPairs.map((ccPair) => ccPair.source); + const [finalAvailableSources, finalAvailableDocumentSets] = + computeAvailableFilters({ + selectedPersona: availableAssistants.find( + (assistant) => assistant.id === liveAssistant?.id + ), + availableSources: availableSources, + availableDocumentSets: documentSets, + }); + // always set the model override for the chat session, when an assistant, llm provider, or user preference exists useEffect(() => { const personaDefault = getLLMProviderOverrideForPersona( @@ -1804,6 +1821,11 @@ export function ChatPage({ setDocumentSelection((documentSelection) => !documentSelection); setShowDocSidebar(false); }; + const [filtersToggled, setFiltersToggled] = useState(false); + const toggleFilters = () => { + setFiltersToggled((filtersToggled) => !filtersToggled); + setDocumentSidebarToggled(true); + }; interface RegenerationRequest { messageId: number; @@ -1965,6 +1987,53 @@ export function ChatPage({ + {!settings?.isMobile && ( +
+ setDocumentSidebarToggled(false)} + selectedMessage={aiMessage} + selectedDocuments={selectedDocuments} + toggleDocumentSelection={toggleDocumentSelection} + clearSelectedDocuments={clearSelectedDocuments} + selectedDocumentTokens={selectedDocumentTokens} + maxTokens={maxTokens} + isLoading={isFetchingChatMessages} + initialWidth={300} + isOpen={documentSidebarToggled} + /> +
+ )} { + toggleDocumentSelectionAspects(); + setDocumentSidebarToggled( + !documentSidebarToggled + ); + }} docs={message.documents} currentPersona={liveAssistant} alternativeAssistant={ @@ -2446,6 +2521,7 @@ export function ChatPage({ )} setShowApiKeyModal(true) } @@ -2500,6 +2576,20 @@ export function ChatPage({ + {!settings?.isMobile && ( +
+ )} )} @@ -2519,8 +2609,9 @@ export function ChatPage({ + {/* Right Sidebar - DocumentSidebar */} - setDocumentSelection(false)} @@ -2532,7 +2623,7 @@ export function ChatPage({ maxTokens={maxTokens} isLoading={isFetchingChatMessages} isOpen={documentSelection} - /> + /> */} ); } diff --git a/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx b/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx index 8fc6c3e4af6..9fbc35697dc 100644 --- a/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx +++ b/web/src/app/chat/documentSidebar/ChatDocumentDisplay.tsx @@ -30,104 +30,68 @@ export function ChatDocumentDisplay({ tokenLimitReached, }: DocumentDisplayProps) { const isInternet = document.is_internet; - // Consider reintroducing null scored docs in the future if (document.score === null) { return null; } + const faviconUrl = + isInternet && document.link + ? `https://www.google.com/s2/favicons?domain=${ + new URL(document.link).hostname + }&sz=32` + : null; + return ( -
-
+
+
- {isInternet ? ( - - ) : ( - - )} -

+

+ {faviconUrl ? ( + Favicon + ) : ( + + )} + + {document.link + ? new URL(document.link).hostname + : document.source_type} + +
+
{document.semantic_identifier || document.document_id} -

-
- {document.score !== null && ( -
- {isAIPick && ( -
- } - popupContent={ -
-
-
- -
-
The AI liked this doc!
-
-
- } - direction="bottom" - style="dark" - /> -
+
+
+ {buildDocumentSummaryDisplay( + document.match_highlights, + document.blurb )} -
- {Math.abs(document.score).toFixed(2)} -
- )} - - {!isInternet && ( - handleSelect(document.document_id)} - isDisabled={tokenLimitReached && !isSelected} - /> - )} -
-
-
- -
-
-

- {/* {buildDocumentSummaryDisplay(document.match_highlights, document.blurb)} */} - test -

-
- {/* - // TODO: find a way to include this - {queryEventId && ( - - )} */} +
+ {!isInternet && ( + handleSelect(document.document_id)} + isDisabled={tokenLimitReached && !isSelected} + /> + )} +
+
); diff --git a/web/src/app/chat/documentSidebar/DocumentSidebar.tsx b/web/src/app/chat/documentSidebar/DocumentSidebar.tsx index 5a6ed956fe6..1225761b3cd 100644 --- a/web/src/app/chat/documentSidebar/DocumentSidebar.tsx +++ b/web/src/app/chat/documentSidebar/DocumentSidebar.tsx @@ -6,8 +6,15 @@ import { removeDuplicateDocs } from "@/lib/documentUtils"; import { Message } from "../interfaces"; import { ForwardedRef, forwardRef } from "react"; import { Separator } from "@/components/ui/separator"; +import { + HorizontalSourceSelector, + SourceSelector, +} from "@/components/search/filtering/Filters"; +import { FilterManager } from "@/lib/hooks"; +import { CCPairBasicInfo, DocumentSet, Tag } from "@/lib/types"; interface DocumentSidebarProps { + filterManager: FilterManager; closeSidebar: () => void; selectedMessage: Message | null; selectedDocuments: DanswerDocument[] | null; @@ -18,6 +25,10 @@ interface DocumentSidebarProps { isLoading: boolean; initialWidth: number; isOpen: boolean; + filtersToggled: boolean; + ccPairs: CCPairBasicInfo[]; + tags: Tag[]; + documentSets: DocumentSet[]; } export const DocumentSidebar = forwardRef( @@ -26,6 +37,7 @@ export const DocumentSidebar = forwardRef( closeSidebar, selectedMessage, selectedDocuments, + filterManager, toggleDocumentSelection, clearSelectedDocuments, selectedDocumentTokens, @@ -33,6 +45,10 @@ export const DocumentSidebar = forwardRef( isLoading, initialWidth, isOpen, + filtersToggled, + ccPairs, + tags, + documentSets, }, ref: ForwardedRef ) => { @@ -51,12 +67,12 @@ export const DocumentSidebar = forwardRef( // space const tokenLimitReached = selectedDocumentTokens > maxTokens - 75; + const hasSelectedDocuments = selectedDocumentIds.length > 0; + return (
{ if (e.target === e.currentTarget) { closeSidebar(); @@ -64,7 +80,7 @@ export const DocumentSidebar = forwardRef( }} >
( >
{popup} -
- {dedupedDocuments.length} Documents -

- Select to add to continuous context +

+

Sources

+
+ + Add to next search + Learn more -

+
- - {currentDocuments ? ( -
+
+ {filtersToggled && ( +
+ {" "} + ccPair.source)} + availableTags={tags} + /> +
+ )} {dedupedDocuments.length > 0 ? ( dedupedDocuments.map((document, ind) => (
( handleSelect={(documentId) => { toggleDocumentSelection( dedupedDocuments.find( - (document) => document.document_id === documentId + (doc) => doc.document_id === documentId )! ); }} @@ -128,34 +157,34 @@ export const DocumentSidebar = forwardRef(
) : ( !isLoading && ( -
+
- When you run ask a question, the retrieved documents will - show up here! + When you ask a question, the retrieved documents will show + up here!
) )}
-
-
- +
+
diff --git a/web/src/app/chat/input/ChatInputBar.tsx b/web/src/app/chat/input/ChatInputBar.tsx index 917998f2062..97a6522b359 100644 --- a/web/src/app/chat/input/ChatInputBar.tsx +++ b/web/src/app/chat/input/ChatInputBar.tsx @@ -1,13 +1,9 @@ import React, { useContext, useEffect, useRef, useState } from "react"; -import { FiPlusCircle, FiPlus, FiInfo, FiX } from "react-icons/fi"; +import { FiPlusCircle, FiPlus, FiInfo, FiX, FiSearch } from "react-icons/fi"; import { ChatInputOption } from "./ChatInputOption"; import { Persona } from "@/app/admin/assistants/interfaces"; import { InputPrompt } from "@/app/admin/prompt-library/interfaces"; -import { - FilterManager, - getDisplayNameForModel, - LlmOverrideManager, -} from "@/lib/hooks"; +import { FilterManager, LlmOverrideManager } from "@/lib/hooks"; import { SelectedFilterDisplay } from "./SelectedFilterDisplay"; import { useChatContext } from "@/components/context/ChatContext"; import { getFinalLLM } from "@/lib/llm/utils"; @@ -17,16 +13,10 @@ import { InputBarPreviewImageProvider, } from "../files/InputBarPreview"; import { - AssistantsIconSkeleton, - CpuIconSkeleton, FileIcon, SendIcon, StopGeneratingIcon, } from "@/components/icons/icons"; -import { IconType } from "react-icons"; -import Popup from "../../../components/popup/Popup"; -import { LlmTab } from "../modal/configuration/LlmTab"; -import { AssistantsTab } from "../modal/configuration/AssistantsTab"; import { DanswerDocument } from "@/lib/search/interfaces"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; import { @@ -40,6 +30,7 @@ import { SettingsContext } from "@/components/settings/SettingsProvider"; import { ChatState } from "../types"; import UnconfiguredProviderText from "@/components/chat_search/UnconfiguredProviderText"; import { useAssistants } from "@/components/context/AssistantsContext"; +import AnimatedToggle from "@/components/search/SearchBar"; const MAX_INPUT_HEIGHT = 200; @@ -68,6 +59,7 @@ export function ChatInputBar({ alternativeAssistant, chatSessionId, inputPrompts, + toggleFilters, }: { showConfigureAPIKey: () => void; openModelSettings: () => void; @@ -90,6 +82,7 @@ export function ChatInputBar({ handleFileUpload: (files: File[]) => void; textAreaRef: React.RefObject; chatSessionId?: string; + toggleFilters?: () => void; }) { useEffect(() => { const textarea = textAreaRef.current; @@ -548,6 +541,13 @@ export function ChatInputBar({ input.click(); }} /> + + {}} />
diff --git a/web/src/app/chat/message/MemoizedTextComponents.tsx b/web/src/app/chat/message/MemoizedTextComponents.tsx index 9ab0e28e3ca..0c85a6a513c 100644 --- a/web/src/app/chat/message/MemoizedTextComponents.tsx +++ b/web/src/app/chat/message/MemoizedTextComponents.tsx @@ -1,8 +1,9 @@ import { Citation } from "@/components/search/results/Citation"; +import { LoadedDanswerDocument } from "@/lib/search/interfaces"; import React, { memo } from "react"; export const MemoizedLink = memo((props: any) => { - const { node, ...rest } = props; + const { node, document, ...rest } = props; const value = rest.children; if (value?.toString().startsWith("*")) { @@ -10,7 +11,11 @@ export const MemoizedLink = memo((props: any) => {
); } else if (value?.toString().startsWith("[")) { - return {rest.children}; + return ( + + {rest.children} + + ); } else { return ( ({ - a: MemoizedLink, + // a: MemoizedLink, + a: ({ node, ...props }: any) => { + const value = props.children?.toString(); + if (value?.startsWith("[") && value?.endsWith("]")) { + const match = value.match(/\[(\d+)\]/); + if (match) { + const index = parseInt(match[1], 10) - 1; + const associatedDoc = docs && docs[index]; + + const icon = getSourceMetadata( + associatedDoc?.source_type || "web" + ).icon({ + size: 18, + }); + + return ( + + {props.children} + + ); + } + } + return {props.children}; + }, p: MemoizedParagraph, code: ({ node, className, children, ...props }: any) => { const codeText = extractCodeText( @@ -425,75 +452,21 @@ export const AIMessage = ({ {docs && docs.length > 0 && (
- {filteredDocs.length} - {docs.length}
{!settings?.isMobile && filteredDocs.length > 0 && - filteredDocs.slice(0, 2).map((doc, ind) => ( - - ))} -
{ - if (toggleDocumentSelection) { - toggleDocumentSelection(); - } - }} - key={-1} - className="cursor-pointer w-[200px] rounded-lg flex-none transition-all duration-500 hover:bg-background-125 bg-text-100 px-4 py-2 border-b" - > -
-

See context

-
- {uniqueSources.map((sourceType, ind) => { - return ( -
- -
- ); - })} -
-
-
- See more -
-
+ filteredDocs + .slice(0, 2) + .map((doc, ind) => ( + + ))} +
diff --git a/web/src/app/chat/page.tsx b/web/src/app/chat/page.tsx index 7894ce651fc..274f362d4f4 100644 --- a/web/src/app/chat/page.tsx +++ b/web/src/app/chat/page.tsx @@ -32,6 +32,7 @@ export default async function Page(props: { defaultAssistantId, shouldShowWelcomeModal, userInputPrompts, + ccPairs, } = data; return ( @@ -44,6 +45,9 @@ export default async function Page(props: { value={{ chatSessions, availableSources, + ccPairs, + documentSets, + tags, availableDocumentSets: documentSets, availableTags: tags, llmProviders, diff --git a/web/src/app/chat/shared_chat_search/Filters.tsx b/web/src/app/chat/shared_chat_search/Filters.tsx new file mode 100644 index 00000000000..74bf8ac39d1 --- /dev/null +++ b/web/src/app/chat/shared_chat_search/Filters.tsx @@ -0,0 +1,240 @@ +import React, { useState } from "react"; +import { DocumentSet, Tag, ValidSources } from "@/lib/types"; +import { SourceMetadata } from "@/lib/search/interfaces"; +import { + GearIcon, + InfoIcon, + MinusIcon, + PlusCircleIcon, + PlusIcon, + defaultTailwindCSS, +} from "@/components/icons/icons"; +import { HoverPopup } from "@/components/HoverPopup"; +import { + FiBook, + FiBookmark, + FiFilter, + FiMap, + FiTag, + FiX, +} from "react-icons/fi"; +import { DateRangePickerValue } from "@/app/ee/admin/performance/DateRangeSelector"; +import { listSourceMetadata } from "@/lib/sources"; +import { SourceIcon } from "@/components/SourceIcon"; +import { TagFilter } from "@/components/search/filtering/TagFilter"; +import { Calendar } from "@/components/ui/calendar"; +import { Popover, PopoverTrigger } from "@/components/ui/popover"; +import { PopoverContent } from "@radix-ui/react-popover"; +import { CalendarIcon } from "lucide-react"; +import { buildDateString, getTimeAgoString } from "@/lib/dateUtils"; + +const SectionTitle = ({ children }: { children: string }) => ( +
{children}
+); + +export interface SourceSelectorProps { + timeRange: DateRangePickerValue | null; + setTimeRange: React.Dispatch< + React.SetStateAction + >; + showDocSidebar?: boolean; + selectedSources: SourceMetadata[]; + setSelectedSources: React.Dispatch>; + selectedDocumentSets: string[]; + setSelectedDocumentSets: React.Dispatch>; + selectedTags: Tag[]; + setSelectedTags: React.Dispatch>; + availableDocumentSets: DocumentSet[]; + existingSources: ValidSources[]; + availableTags: Tag[]; +} + +export function SourceSelector({ + timeRange, + setTimeRange, + selectedSources, + setSelectedSources, + selectedDocumentSets, + setSelectedDocumentSets, + selectedTags, + setSelectedTags, + availableDocumentSets, + existingSources, + availableTags, + showDocSidebar, +}: SourceSelectorProps) { + const handleSelect = (source: SourceMetadata) => { + setSelectedSources((prev: SourceMetadata[]) => { + if ( + prev.map((source) => source.internalName).includes(source.internalName) + ) { + return prev.filter((s) => s.internalName !== source.internalName); + } else { + return [...prev, source]; + } + }); + }; + + const handleDocumentSetSelect = (documentSetName: string) => { + setSelectedDocumentSets((prev: string[]) => { + if (prev.includes(documentSetName)) { + return prev.filter((s) => s !== documentSetName); + } else { + return [...prev, documentSetName]; + } + }); + }; + + let allSourcesSelected = selectedSources.length > 0; + + const toggleAllSources = () => { + if (allSourcesSelected) { + setSelectedSources([]); + } else { + const allSources = listSourceMetadata().filter((source) => + existingSources.includes(source.internalName) + ); + setSelectedSources(allSources); + } + }; + + return ( +
+
+ +
+ + + +
+ Time Range +

+ {getTimeAgoString(timeRange?.from!) || "Select a time range"} +

+
+
+ + { + const initialDate = daterange?.from || new Date(); + const endDate = daterange?.to || new Date(); + setTimeRange({ + from: initialDate, + to: endDate, + selectValue: timeRange?.selectValue || "", + }); + }} + className="rounded-md " + /> + +
+ + {availableTags.length > 0 && ( + <> +
+ Tags +
+ + + )} + + {existingSources.length > 0 && ( +
+
+
+

Sources

+ +
+
+
+ {listSourceMetadata() + .filter((source) => existingSources.includes(source.internalName)) + .map((source) => ( +
source.internalName) + .includes(source.internalName) + ? "bg-hover" + : "hover:bg-hover-light") + } + onClick={() => handleSelect(source)} + > + + + {source.displayName} + +
+ ))} +
+
+ )} + + {availableDocumentSets.length > 0 && ( + <> +
+ Knowledge Sets +
+
+ {availableDocumentSets.map((documentSet) => ( +
+
handleDocumentSetSelect(documentSet.name)} + > + + +
+ } + popupContent={ +
+
Description
+
{documentSet.description}
+
+ } + classNameModifications="-ml-2" + /> + {documentSet.name} +
+
+ ))} +
+ + )} +
+ ); +} diff --git a/web/src/components/chat_search/sources/SidebarSource.tsx b/web/src/components/chat_search/sources/SidebarSource.tsx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/web/src/components/chat_search/sources/firstsourcecard.tsx b/web/src/components/chat_search/sources/firstsourcecard.tsx new file mode 100644 index 00000000000..87aa11e23b1 --- /dev/null +++ b/web/src/components/chat_search/sources/firstsourcecard.tsx @@ -0,0 +1,79 @@ +import { InternetSearchIcon } from "@/components/InternetSearchIcon"; +import { SourceIcon } from "@/components/SourceIcon"; +import { DanswerDocument } from "@/lib/search/interfaces"; + +export default function FirstSourceCard({ doc }: { doc: DanswerDocument }) { + return ( + +
+ {doc.is_internet ? ( + + ) : ( + + )} +

+ {(doc.semantic_identifier || doc.document_id).slice(0, 12).trim()} + {(doc.semantic_identifier || doc.document_id).length > 12 && ( + ... + )} +

+ {/* {doc.source_type} */} +
+
+
+ {doc.blurb} +
+
+ ); +} + +interface SeeMoreBlockProps { + documentSelectionToggled: boolean; + toggleDocumentSelection?: () => void; + uniqueSources: DanswerDocument["source_type"][]; +} + +export function SeeMoreBlock({ + documentSelectionToggled, + toggleDocumentSelection, + uniqueSources, +}: SeeMoreBlockProps) { + return ( +
+
+

+ {documentSelectionToggled ? "Hide sources" : "See context"} +

+
+ {uniqueSources.map((sourceType, ind) => ( +
+
+ +
+
+ ))} +
+
+
+
+ See more +
+
+ ); +} diff --git a/web/src/components/context/ChatContext.tsx b/web/src/components/context/ChatContext.tsx index 9ecd0dcefc0..4fb2fbd8e94 100644 --- a/web/src/components/context/ChatContext.tsx +++ b/web/src/components/context/ChatContext.tsx @@ -1,7 +1,13 @@ "use client"; import React, { createContext, useContext, useState } from "react"; -import { DocumentSet, Tag, User, ValidSources } from "@/lib/types"; +import { + CCPairBasicInfo, + DocumentSet, + Tag, + User, + ValidSources, +} from "@/lib/types"; import { ChatSession } from "@/app/chat/interfaces"; import { Persona } from "@/app/admin/assistants/interfaces"; import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; @@ -12,6 +18,9 @@ import { personaComparator } from "@/app/admin/assistants/lib"; interface ChatContextProps { chatSessions: ChatSession[]; availableSources: ValidSources[]; + ccPairs: CCPairBasicInfo[]; + tags: Tag[]; + documentSets: DocumentSet[]; availableDocumentSets: DocumentSet[]; availableTags: Tag[]; llmProviders: LLMProviderDescriptor[]; diff --git a/web/src/components/context/SearchContext.tsx b/web/src/components/context/SearchContext.tsx index a46fbed24ca..3150747b483 100644 --- a/web/src/components/context/SearchContext.tsx +++ b/web/src/components/context/SearchContext.tsx @@ -8,9 +8,9 @@ import { ChatSession } from "@/app/chat/interfaces"; interface SearchContextProps { querySessions: ChatSession[]; ccPairs: CCPairBasicInfo[]; + tags: Tag[]; documentSets: DocumentSet[]; assistants: Persona[]; - tags: Tag[]; agenticSearchEnabled: boolean; disabledAgentic: boolean; initiallyToggled: boolean; diff --git a/web/src/components/search/DocumentDisplay.tsx b/web/src/components/search/DocumentDisplay.tsx index 1cf41bcf5d6..666ac4e1b0d 100644 --- a/web/src/components/search/DocumentDisplay.tsx +++ b/web/src/components/search/DocumentDisplay.tsx @@ -1,8 +1,10 @@ "use client"; - +import { InternetSearchIcon } from "@/components/InternetSearchIcon"; +import React from "react"; import { DanswerDocument, DocumentRelevance, + LoadedDanswerDocument, SearchDanswerDocument, } from "@/lib/search/interfaces"; import { DocumentFeedbackBlock } from "./DocumentFeedbackBlock"; @@ -23,10 +25,7 @@ export const buildDocumentSummaryDisplay = ( matchHighlights: string[], blurb: string ) => { - console.log("matchHighlights"); - console.log(matchHighlights); - console.log(blurb); - if (matchHighlights.length === 0) { + if (!matchHighlights || matchHighlights.length === 0) { return blurb; } @@ -389,3 +388,34 @@ export const AgenticDocumentDisplay = ({
); }; + +export function CompactDocumentCard({ + document, +}: { + document: LoadedDanswerDocument; +}) { + console.log("doc icon"); + console.log(document.icon); + return ( + +
+ {document.icon ? document.icon : "NO"} +

+ {(document.semantic_identifier || document.document_id) + .slice(0, 12) + .trim()} + {(document.semantic_identifier || document.document_id).length > + 12 && ...} +

+
+
+ {document.blurb} +
+
+ ); +} diff --git a/web/src/components/search/SearchBar.tsx b/web/src/components/search/SearchBar.tsx index 6a346eaf7a8..4dff857ba50 100644 --- a/web/src/components/search/SearchBar.tsx +++ b/web/src/components/search/SearchBar.tsx @@ -17,6 +17,12 @@ interface FullSearchBarProps { showingSidebar: boolean; } +import { + TooltipProvider, + Tooltip, + TooltipTrigger, + TooltipContent, +} from "@/components/ui/tooltip"; import { useRef } from "react"; import { SendIcon } from "../icons/icons"; import { Separator } from "@/components/ui/separator"; @@ -28,61 +34,65 @@ import { CCPairBasicInfo, DocumentSet, Tag } from "@/lib/types"; export const AnimatedToggle = ({ isOn, handleToggle, + direction = "top", }: { isOn: boolean; handleToggle: () => void; + direction?: "bottom" | "top"; }) => { const commandSymbol = KeyboardSymbol(); const containerRef = useRef(null); const contentRef = useRef(null); return ( - -

- Agentic Search -

-

- Our most powerful search, have an AI agent guide you to pinpoint - exactly what you're looking for. -

- -

Fast Search

-

- Get quality results immediately, best suited for instant access to - your documents. -

-

Shortcut: ({commandSymbol}/)

-
- } - > -
-
- {/* Toggle switch */} + + +
-
+
+
+
+
+

Agentic

+
-

Agentic

-
-
- + + +
+

+ Agentic Search +

+

+ Our most powerful search, have an AI agent guide you to pinpoint + exactly what you're looking for. +

+ +

+ Fast Search +

+

+ Get quality results immediately, best suited for instant access to + your documents. +

+

Shortcut: ({commandSymbol}/)

+
+
+ + ); }; diff --git a/web/src/components/search/results/Citation.tsx b/web/src/components/search/results/Citation.tsx index 61836801578..b79ef77d9d2 100644 --- a/web/src/components/search/results/Citation.tsx +++ b/web/src/components/search/results/Citation.tsx @@ -1,55 +1,76 @@ -import { CustomTooltip } from "@/components/tooltip/CustomTooltip"; import { ReactNode } from "react"; - +import { CompactDocumentCard } from "../DocumentDisplay"; +import { LoadedDanswerDocument } from "@/lib/search/interfaces"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; // NOTE: This is the preivous version of the citations which works just fine export function Citation({ children, link, + document, index, }: { link?: string; children?: JSX.Element | string | null | ReactNode; index?: number; + document: LoadedDanswerDocument; }) { const innerText = children ? children?.toString().split("[")[1].split("]")[0] : index; - if (link != "") { + if (link) { return ( - {link}
} - > - (link ? window.open(link, "_blank") : undefined)} - className="cursor-pointer inline ml-1 align-middle" - > - - + + + window.open(link, "_blank")} + className="cursor-pointer inline ml-1 align-middle" > - {innerText} - - - - + + + {innerText} + + + + + + {document && } + {/*

{link}

*/} +
+ + ); } else { return ( - This doc doesn't have a link!
}> -
- - - {innerText} - - -
- + + + +
+ {document && } + + + {innerText} + + +
+
+ + {document && } + +
+
); } } diff --git a/web/src/components/tooltip/CustomTooltip.tsx b/web/src/components/tooltip/CustomTooltip.tsx index 3737d0c1ee7..001b9fdf502 100644 --- a/web/src/components/tooltip/CustomTooltip.tsx +++ b/web/src/components/tooltip/CustomTooltip.tsx @@ -131,7 +131,7 @@ export const CustomTooltip = ({ transform -translate-x-1/2 text-sm ${ light - ? "text-gray-800 bg-background-200" + ? "text-text-800 bg-background-200" : "text-white bg-background-800" } rounded-lg shadow-lg`} diff --git a/web/src/lib/search/interfaces.ts b/web/src/lib/search/interfaces.ts index a24cc222652..92492662c8c 100644 --- a/web/src/lib/search/interfaces.ts +++ b/web/src/lib/search/interfaces.ts @@ -62,6 +62,9 @@ export interface DanswerDocument { is_internet: boolean; validationState?: null | "good" | "bad"; } +export interface LoadedDanswerDocument extends DanswerDocument { + icon: React.FC<{ size?: number; className?: string }>; +} export interface SearchDanswerDocument extends DanswerDocument { is_relevant: boolean;