diff --git a/web/src/app/admin/configuration/llm/interfaces.ts b/web/src/app/admin/configuration/llm/interfaces.ts index 2d0d49196..33fa94d7f 100644 --- a/web/src/app/admin/configuration/llm/interfaces.ts +++ b/web/src/app/admin/configuration/llm/interfaces.ts @@ -1,3 +1,13 @@ +import { + AnthropicIcon, + AWSIcon, + AzureIcon, + CPUIcon, + OpenAIIcon, + OpenSourceIcon, +} from "@/components/icons/icons"; +import { FaRobot } from "react-icons/fa"; + export interface CustomConfigKey { name: string; description: string | null; @@ -53,3 +63,18 @@ export interface LLMProviderDescriptor { groups: number[]; display_model_names: string[] | null; } + +export const getProviderIcon = (providerName: string) => { + switch (providerName) { + case "openai": + return OpenAIIcon; + case "anthropic": + return AnthropicIcon; + case "bedrock": + return AWSIcon; + case "azure": + return AzureIcon; + default: + return CPUIcon; + } +}; diff --git a/web/src/app/admin/indexing/status/CCPairIndexingStatusTable.tsx b/web/src/app/admin/indexing/status/CCPairIndexingStatusTable.tsx index 705f76079..b62074a72 100644 --- a/web/src/app/admin/indexing/status/CCPairIndexingStatusTable.tsx +++ b/web/src/app/admin/indexing/status/CCPairIndexingStatusTable.tsx @@ -482,7 +482,7 @@ export function CCPairIndexingStatusTable({ if (sourceMatches || matchingConnectors.length > 0) { return ( -
+
@@ -65,6 +66,7 @@ export function ChatBanner() { className="absolute top-0 left-0 invisible w-full" > diff --git a/web/src/app/chat/input/ChatInputBar.tsx b/web/src/app/chat/input/ChatInputBar.tsx index b579abefe..66fe490c0 100644 --- a/web/src/app/chat/input/ChatInputBar.tsx +++ b/web/src/app/chat/input/ChatInputBar.tsx @@ -338,10 +338,10 @@ export function ChatInputBar({ updateInputPrompt(currentPrompt); }} > -

{currentPrompt.prompt}

-

+

{currentPrompt.prompt}:

+

{currentPrompt.id == selectedAssistant.id && "(default) "} - {currentPrompt.content} + {currentPrompt.content?.trim()}

))} diff --git a/web/src/app/chat/sessionSidebar/ChatSessionDisplay.tsx b/web/src/app/chat/sessionSidebar/ChatSessionDisplay.tsx index df7ddee95..669a183d7 100644 --- a/web/src/app/chat/sessionSidebar/ChatSessionDisplay.tsx +++ b/web/src/app/chat/sessionSidebar/ChatSessionDisplay.tsx @@ -46,6 +46,7 @@ export function ChatSessionDisplay({ showDeleteModal?: (chatSession: ChatSession) => void; }) { const router = useRouter(); + const [isHovering, setIsHovering] = useState(false); const [isRenamingChat, setIsRenamingChat] = useState(false); const [isMoreOptionsDropdownOpen, setIsMoreOptionsDropdownOpen] = useState(false); @@ -97,6 +98,11 @@ export function ChatSessionDisplay({ setIsHovering(true)} + onMouseLeave={() => { + setIsMoreOptionsDropdownOpen(false); + setIsHovering(false); + }} onClick={() => { if (settings?.isMobile && closeSidebar) { closeSidebar(); @@ -145,7 +151,7 @@ export function ChatSessionDisplay({

)} - {isSelected && + {isHovering && (isRenamingChat ? (
-
{ - setIsMoreOptionsDropdownOpen( - !isMoreOptionsDropdownOpen - ); - }} - className={"-my-1"} - > - - setIsMoreOptionsDropdownOpen(open) - } - content={ -
- -
- } - popover={ -
- {showShareModal && ( - showShareModal(chatSession)} - /> - )} - setIsRenamingChat(true)} - /> -
- } - requiresContentPadding - sideOffset={6} - triggerMaxWidth - /> -
+ {search ? ( + showDeleteModal && ( +
showDeleteModal(chatSession)} + className={`p-1 -m-1 rounded ml-1`} + > + +
+ ) + ) : ( +
{ + setIsMoreOptionsDropdownOpen( + !isMoreOptionsDropdownOpen + ); + }} + className="-my-1" + > + + setIsMoreOptionsDropdownOpen(open) + } + content={ +
+ +
+ } + popover={ +
+ {showShareModal && ( + showShareModal(chatSession)} + /> + )} + {!search && ( + setIsRenamingChat(true)} + /> + )} + {showDeleteModal && ( + + showDeleteModal(chatSession) + } + /> + )} +
+ } + requiresContentPadding + sideOffset={6} + triggerMaxWidth + /> +
+ )}
- {showDeleteModal && ( -
showDeleteModal(chatSession)} - className={`hover:bg-black/10 p-1 -m-1 rounded ml-1`} - > - -
- )}
))} diff --git a/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx b/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx index a5693ab47..4f8d31d39 100644 --- a/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx +++ b/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx @@ -53,9 +53,14 @@ const ToggleSwitch = () => { onClick={() => handleTabChange("search")} > -
+
Search -
{commandSymbol}S
+
+ + {commandSymbol} + + S +
diff --git a/web/src/app/globals.css b/web/src/app/globals.css index fdb9d7985..e0e618e46 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -36,7 +36,7 @@ .include-scrollbar { scrollbar-width: thin; - scrollbar-color: #888 #f1f1f1; + scrollbar-color: #888 transparent; } .inputscroll::-webkit-scrollbar-track { diff --git a/web/src/components/chat_search/MinimalMarkdown.tsx b/web/src/components/chat_search/MinimalMarkdown.tsx index 6fadc979c..9df0260f4 100644 --- a/web/src/components/chat_search/MinimalMarkdown.tsx +++ b/web/src/components/chat_search/MinimalMarkdown.tsx @@ -1,3 +1,4 @@ +import { CodeBlock } from "@/app/chat/message/CodeBlock"; import React from "react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; @@ -5,11 +6,13 @@ import remarkGfm from "remark-gfm"; interface MinimalMarkdownProps { content: string; className?: string; + useCodeBlock?: boolean; } export const MinimalMarkdown: React.FC = ({ content, className = "", + useCodeBlock = false, }) => { return ( = ({ p: ({ node, ...props }) => (

), + code: useCodeBlock + ? (props) => ( + + ) + : (props) => , }} remarkPlugins={[remarkGfm]} > diff --git a/web/src/components/llm/LLMList.tsx b/web/src/components/llm/LLMList.tsx index 90c904c58..191a89485 100644 --- a/web/src/components/llm/LLMList.tsx +++ b/web/src/components/llm/LLMList.tsx @@ -1,7 +1,10 @@ import React from "react"; import { getDisplayNameForModel } from "@/lib/hooks"; import { structureValue } from "@/lib/llm/utils"; -import { LLMProviderDescriptor } from "@/app/admin/configuration/llm/interfaces"; +import { + getProviderIcon, + LLMProviderDescriptor, +} from "@/app/admin/configuration/llm/interfaces"; interface LlmListProps { llmProviders: LLMProviderDescriptor[]; @@ -9,6 +12,7 @@ interface LlmListProps { onSelect: (value: string | null) => void; userDefault?: string | null; scrollable?: boolean; + hideProviderIcon?: boolean; } export const LlmList: React.FC = ({ @@ -19,7 +23,11 @@ export const LlmList: React.FC = ({ scrollable, }) => { const llmOptionsByProvider: { - [provider: string]: { name: string; value: string }[]; + [provider: string]: { + name: string; + value: string; + icon: React.FC<{ size?: number; className?: string }>; + }[]; } = {}; const uniqueModelNames = new Set(); @@ -39,6 +47,7 @@ export const LlmList: React.FC = ({ llmProvider.provider, modelName ), + icon: getProviderIcon(llmProvider.provider), }); } } @@ -67,17 +76,18 @@ export const LlmList: React.FC = ({ User Default (currently {getDisplayNameForModel(userDefault)}) )} - {llmOptions.map(({ name, value }, index) => ( + {llmOptions.map(({ name, icon, value }, index) => ( ))} diff --git a/web/src/components/search/SearchBar.tsx b/web/src/components/search/SearchBar.tsx index 98533ffea..7d62b0992 100644 --- a/web/src/components/search/SearchBar.tsx +++ b/web/src/components/search/SearchBar.tsx @@ -71,7 +71,7 @@ export const AnimatedToggle = ({ Get quality results immediately, best suited for instant access to your documents.

-

Shortcut: ({commandSymbol}/)

+

Shortcut: ({commandSymbol}/)

} > diff --git a/web/src/components/search/SearchSection.tsx b/web/src/components/search/SearchSection.tsx index feae79cdf..0827cbab7 100644 --- a/web/src/components/search/SearchSection.tsx +++ b/web/src/components/search/SearchSection.tsx @@ -34,8 +34,9 @@ import FixedLogo from "@/app/chat/shared_chat_search/FixedLogo"; import { usePopup } from "../admin/connectors/Popup"; import { FeedbackType } from "@/app/chat/types"; import { FeedbackModal } from "@/app/chat/modal/FeedbackModal"; -import { handleChatFeedback } from "@/app/chat/lib"; +import { deleteChatSession, handleChatFeedback } from "@/app/chat/lib"; import SearchAnswer from "./SearchAnswer"; +import { DeleteEntityModal } from "../modals/DeleteEntityModal"; export type searchState = | "input" @@ -511,7 +512,12 @@ export const SearchSection = ({ }; const [firstSearch, setFirstSearch] = useState(true); const [searchState, setSearchState] = useState("input"); + const [deletingChatSession, setDeletingChatSession] = + useState(); + const showDeleteModal = (chatSession: ChatSession) => { + setDeletingChatSession(chatSession); + }; // Used to maintain a "time out" for history sidebar so our existing refs can have time to process change const [untoggled, setUntoggled] = useState(false); @@ -591,6 +597,24 @@ export const SearchSection = ({ <>
{popup} + + {deletingChatSession && ( + setDeletingChatSession(null)} + onSubmit={async () => { + const response = await deleteChatSession(deletingChatSession.id); + if (response.ok) { + setDeletingChatSession(null); + // go back to the main page + router.push("/search"); + } else { + alert("Failed to delete chat session"); + } + }} + /> + )} {currentFeedback && (
setQuery("")} page="search" @@ -672,7 +697,7 @@ export const SearchSection = ({ (ccPairs.length > 0 || documentSets.length > 0) && ( Tags
-
+
{filteredTags.length > 0 ? ( filteredTags.map((tag) => (
{ header = <>; body = ( - - {replaceNewlines(props.answer || "")} - + ); // error while building answer (NOTE: if error occurs during quote generation @@ -63,12 +62,7 @@ export const AnswerSection = (props: AnswerSectionProps) => { status = "success"; header = <>; body = ( - - {replaceNewlines(props.answer)} - + ); } diff --git a/web/src/lib/assistants/fetchPersonaEditorInfoSS.ts b/web/src/lib/assistants/fetchPersonaEditorInfoSS.ts index f779b2ed6..9873e0a25 100644 --- a/web/src/lib/assistants/fetchPersonaEditorInfoSS.ts +++ b/web/src/lib/assistants/fetchPersonaEditorInfoSS.ts @@ -2,7 +2,10 @@ import { Persona } from "@/app/admin/assistants/interfaces"; import { CCPairBasicInfo, DocumentSet, User } from "../types"; import { getCurrentUserSS } from "../userSS"; import { fetchSS } from "../utilsSS"; -import { FullLLMProvider } from "@/app/admin/configuration/llm/interfaces"; +import { + FullLLMProvider, + getProviderIcon, +} from "@/app/admin/configuration/llm/interfaces"; import { ToolSnapshot } from "../tools/interfaces"; import { fetchToolsSS } from "../tools/fetchTools"; import { @@ -94,17 +97,7 @@ export async function fetchAssistantEditorInfoSS( } for (const provider of llmProviders) { - if (provider.provider == "openai") { - provider.icon = OpenAIIcon; - } else if (provider.provider == "anthropic") { - provider.icon = AnthropicIcon; - } else if (provider.provider == "bedrock") { - provider.icon = AWSIcon; - } else if (provider.provider == "azure") { - provider.icon = AzureIcon; - } else { - provider.icon = OpenSourceIcon; - } + provider.icon = getProviderIcon(provider.provider); } const existingPersona = personaResponse