From eb9bb56829873c43b6c32c6709ea86c8eb07c185 Mon Sep 17 00:00:00 2001 From: pablodanswer Date: Tue, 30 Jul 2024 17:13:50 -0700 Subject: [PATCH] Add initial mobile support (#1962) --- web/src/app/admin/settings/interfaces.ts | 1 + web/src/app/assistants/SidebarWrapper.tsx | 14 +- web/src/app/assistants/ToolsDisplay.tsx | 2 +- .../app/assistants/mine/AssistantsList.tsx | 2 +- web/src/app/chat/ChatIntro.tsx | 2 +- web/src/app/chat/ChatPage.tsx | 949 +++++++++--------- .../chat/documentSidebar/DocumentSidebar.tsx | 40 +- web/src/app/chat/input/ChatInputBar.tsx | 32 +- web/src/app/chat/input/ChatInputOption.tsx | 6 +- web/src/app/chat/lib.tsx | 1 + web/src/app/chat/message/Messages.tsx | 17 +- web/src/app/chat/message/SkippedSearch.tsx | 7 +- web/src/app/chat/page.tsx | 7 +- .../sessionSidebar/ChatSessionDisplay.tsx | 11 +- .../chat/sessionSidebar/HistorySidebar.tsx | 14 +- web/src/app/chat/sessionSidebar/PagesTab.tsx | 5 +- .../app/chat/shared_chat_search/FixedLogo.tsx | 6 +- .../shared_chat_search/FunctionalWrapper.tsx | 27 +- web/src/app/globals.css | 4 + web/src/app/layout.tsx | 29 +- web/src/app/search/page.tsx | 1 - web/src/components/UserDropdown.tsx | 11 +- .../admin/connectors/ConnectoForm.tsx | 0 web/src/components/chat_search/Header.tsx | 36 +- web/src/components/chat_search/hooks.ts | 27 +- web/src/components/header/Header.tsx | 1 - web/src/components/popover/styles.css | 1 + web/src/components/popup/Popup.tsx | 49 +- web/src/components/search/DocumentDisplay.tsx | 16 +- web/src/components/search/SearchBar.tsx | 13 +- web/src/components/search/SearchSection.tsx | 163 +-- .../components/settings/SettingsProvider.tsx | 16 +- web/src/components/settings/lib.ts | 1 + web/tailwind-themes/tailwind.config.js | 2 + 34 files changed, 837 insertions(+), 676 deletions(-) delete mode 100644 web/src/components/admin/connectors/ConnectoForm.tsx diff --git a/web/src/app/admin/settings/interfaces.ts b/web/src/app/admin/settings/interfaces.ts index 02372ce2c74e..571cd193b7f5 100644 --- a/web/src/app/admin/settings/interfaces.ts +++ b/web/src/app/admin/settings/interfaces.ts @@ -19,4 +19,5 @@ export interface CombinedSettings { settings: Settings; enterpriseSettings: EnterpriseSettings | null; customAnalyticsScript: string | null; + isMobile?: boolean; } diff --git a/web/src/app/assistants/SidebarWrapper.tsx b/web/src/app/assistants/SidebarWrapper.tsx index a5a3f7857234..7f12d86efea1 100644 --- a/web/src/app/assistants/SidebarWrapper.tsx +++ b/web/src/app/assistants/SidebarWrapper.tsx @@ -7,12 +7,13 @@ import { Folder } from "@/app/chat/folders/interfaces"; import { User } from "@/lib/types"; import Cookies from "js-cookie"; import { SIDEBAR_TOGGLED_COOKIE_NAME } from "@/components/resizable/constants"; -import { ReactNode, useEffect, useRef, useState } from "react"; +import { ReactNode, useContext, useEffect, useRef, useState } from "react"; import { useSidebarVisibility } from "@/components/chat_search/hooks"; import FunctionalHeader from "@/components/chat_search/Header"; import { useRouter } from "next/navigation"; import { pageType } from "../chat/sessionSidebar/types"; import FixedLogo from "../chat/shared_chat_search/FixedLogo"; +import { SettingsContext } from "@/components/settings/SettingsProvider"; interface SidebarWrapperProps { chatSessions?: ChatSession[]; @@ -55,11 +56,13 @@ export default function SidebarWrapper({ const sidebarElementRef = useRef(null); + const settings = useContext(SettingsContext); useSidebarVisibility({ toggledSidebar, sidebarElementRef, showDocSidebar, setShowDocSidebar, + mobile: settings?.isMobile, }); const innerSidebarElementRef = useRef(null); @@ -90,9 +93,7 @@ export default function SidebarWrapper({ flex-none fixed left-0 - z-20 - overflow-y-hidden - sidebar + z-30 bg-background-100 h-screen transition-all @@ -119,10 +120,10 @@ export default function SidebarWrapper({ -
+
@@ -138,6 +139,7 @@ export default function SidebarWrapper({ ease-in-out ${toggledSidebar ? "w-[250px]" : "w-[0px]"}`} /> +
{content(contentProps)}
diff --git a/web/src/app/assistants/ToolsDisplay.tsx b/web/src/app/assistants/ToolsDisplay.tsx index 63ed3ff1e5b1..9650dacbb990 100644 --- a/web/src/app/assistants/ToolsDisplay.tsx +++ b/web/src/app/assistants/ToolsDisplay.tsx @@ -47,7 +47,7 @@ export function AssistantTools({ hovered?: boolean; }) { return ( -
+
0 && "py-1"} ${!list ? "font-semibold" : "text-subtle text-sm"}`} > diff --git a/web/src/app/assistants/mine/AssistantsList.tsx b/web/src/app/assistants/mine/AssistantsList.tsx index 52aa3ff8e0bc..29d67a7c7212 100644 --- a/web/src/app/assistants/mine/AssistantsList.tsx +++ b/web/src/app/assistants/mine/AssistantsList.tsx @@ -287,7 +287,7 @@ export function AssistantsList({ user, assistants }: AssistantsListProps) { return ( <> {popup} -
+
My Assistants
diff --git a/web/src/app/chat/ChatIntro.tsx b/web/src/app/chat/ChatIntro.tsx index e6a40b1fd21b..27353aa340f0 100644 --- a/web/src/app/chat/ChatIntro.tsx +++ b/web/src/app/chat/ChatIntro.tsx @@ -38,7 +38,7 @@ export function ChatIntro({ return ( <>
-
+
diff --git a/web/src/app/chat/ChatPage.tsx b/web/src/app/chat/ChatPage.tsx index dd208b882917..7338c72e0a31 100644 --- a/web/src/app/chat/ChatPage.tsx +++ b/web/src/app/chat/ChatPage.tsx @@ -85,7 +85,7 @@ export function ChatPage({ defaultSelectedAssistantId, toggledSidebar, }: { - toggle: () => void; + toggle: (toggled?: boolean) => void; documentSidebarInitialWidth?: number; defaultSelectedAssistantId?: number; toggledSidebar: boolean; @@ -524,15 +524,6 @@ export function ChatPage({ const distance = 500; // distance that should "engage" the scroll const debounce = 100; // time for debouncing - useScrollonStream({ - isStreaming, - scrollableDivRef, - scrollDist, - endDivRef, - distance, - debounce, - }); - const [hasPerformedInitialScroll, setHasPerformedInitialScroll] = useState( existingChatSessionId === null ); @@ -644,7 +635,6 @@ export function ChatPage({ alternativeAssistantOverride?: Persona | null; } = {}) => { setAlternativeGeneratingAssistant(alternativeAssistantOverride); - clientScrollToBottom(); let currChatSessionId: number; let isNewSession = chatSessionIdRef.current === null; @@ -1059,6 +1049,10 @@ export function ChatPage({ toggle(); }; + const removeToggle = () => { + setShowDocSidebar(false); + toggle(false); + }; const sidebarElementRef = useRef(null); @@ -1067,6 +1061,17 @@ export function ChatPage({ sidebarElementRef, showDocSidebar, setShowDocSidebar, + setToggled: removeToggle, + mobile: settings?.isMobile, + }); + + useScrollonStream({ + isStreaming, + scrollableDivRef, + scrollDist, + endDivRef, + distance, + debounce, }); useEffect(() => { @@ -1119,96 +1124,100 @@ export function ChatPage({ <> - {/* ChatPopup is a custom popup that displays a admin-specified message on initial user visit. Only used in the EE version of the app. */} - -
-
-
- + {currentFeedback && ( + setCurrentFeedback(null)} + onSubmit={({ message, predefinedFeedback }) => { + onFeedback( + currentFeedback[1], + currentFeedback[0], + message, + predefinedFeedback + ); + setCurrentFeedback(null); + }} + /> + )} + {sharingModalVisible && chatSessionIdRef.current !== null && ( + setSharingModalVisible(false)} + onShare={(shared) => + setChatSessionSharedStatus( + shared + ? ChatSessionSharedStatus.Public + : ChatSessionSharedStatus.Private + ) + } + /> + )} +
+
+
+
+
+ +
+
-
-
- {popup} - {currentFeedback && ( - setCurrentFeedback(null)} - onSubmit={({ message, predefinedFeedback }) => { - onFeedback( - currentFeedback[1], - currentFeedback[0], - message, - predefinedFeedback - ); - setCurrentFeedback(null); - }} - /> - )} - {sharingModalVisible && chatSessionIdRef.current !== null && ( - setSharingModalVisible(false)} - onShare={(shared) => - setChatSessionSharedStatus( - shared - ? ChatSessionSharedStatus.Public - : ChatSessionSharedStatus.Private - ) - } - /> - )} +
+ {popup} -
- {liveAssistant && ( - - )} -
-
+ {liveAssistant && ( + + )} +
+
- -
- {documentSidebarInitialWidth !== undefined ? ( - - {({ getRootProps }) => ( -
-
+ +
+ {documentSidebarInitialWidth !== undefined ? ( + + {({ getRootProps }) => ( +
+ {!settings?.isMobile && ( +
-
- {/* */} + >
+ )} +
- {/* ChatBanner is a custom banner that displays a admin-specified message at + {/* */} +
+ {/* ChatBanner is a custom banner that displays a admin-specified message at the top of the chat page. Oly used in the EE version of the app. */} - {messageHistory.length === 0 && - !isFetchingChatMessages && - !isStreaming && ( - - )} -
- {messageHistory.map((message, i) => { - const messageMap = completeMessageDetail.messageMap; - const messageReactComponentKey = `${i}-${completeMessageDetail.sessionId}`; - if (message.type === "user") { - const parentMessage = message.parentMessageId - ? messageMap.get(message.parentMessageId) - : null; - return ( -
- { - const parentMessageId = - message.parentMessageId!; - const parentMessage = - messageMap.get(parentMessageId)!; - upsertToCompleteMessageMap({ - messages: [ - { - ...parentMessage, - latestChildMessageId: null, - }, - ], - }); - onSubmit({ - messageIdToResend: - message.messageId || undefined, - messageOverride: editedContent, - }); - }} - onMessageSelection={(messageId) => { - const newCompleteMessageMap = new Map( - messageMap - ); - newCompleteMessageMap.get( - message.parentMessageId! - )!.latestChildMessageId = messageId; - setCompleteMessageDetail({ - sessionId: - completeMessageDetail.sessionId, - messageMap: newCompleteMessageMap, - }); - setSelectedMessageForDocDisplay( - messageId - ); - // set message as latest so we can edit this message - // and so it sticks around on page reload - setMessageAsLatest(messageId); - }} - /> -
- ); - } else if (message.type === "assistant") { - const isShowingRetrieved = - (selectedMessageForDocDisplay !== null && - selectedMessageForDocDisplay === - message.messageId) || - (selectedMessageForDocDisplay === - TEMP_USER_MESSAGE_ID && - i === messageHistory.length - 1); - const previousMessage = - i !== 0 ? messageHistory[i - 1] : null; - - const currentAlternativeAssistant = - message.alternateAssistantID != null - ? availableAssistants.find( - (persona) => - persona.id == - message.alternateAssistantID - ) + {messageHistory.length === 0 && + !isFetchingChatMessages && + !isStreaming && ( + + )} +
+ {messageHistory.map((message, i) => { + const messageMap = + completeMessageDetail.messageMap; + const messageReactComponentKey = `${i}-${completeMessageDetail.sessionId}`; + if (message.type === "user") { + const parentMessage = message.parentMessageId + ? messageMap.get(message.parentMessageId) : null; - - return ( -
- 0) === true - } - handleFeedback={ - i === messageHistory.length - 1 && - isStreaming - ? undefined - : (feedbackType) => - setCurrentFeedback([ - feedbackType, - message.messageId as number, - ]) - } - handleSearchQueryEdit={ - i === messageHistory.length - 1 && - !isStreaming - ? (newQuery) => { - if (!previousMessage) { - setPopup({ - type: "error", - message: - "Cannot edit query of first message - please refresh the page and try again.", - }); - return; - } - - if ( - previousMessage.messageId === null - ) { - setPopup({ - type: "error", - message: - "Cannot edit query of a pending message - please wait a few seconds and try again.", - }); - return; - } - onSubmit({ - messageIdToResend: - previousMessage.messageId, - queryOverride: newQuery, - alternativeAssistantOverride: - currentAlternativeAssistant, - }); - } - : undefined - } - isCurrentlyShowingRetrieved={ - isShowingRetrieved - } - handleShowRetrieved={(messageNumber) => { - if (isShowingRetrieved) { - setSelectedMessageForDocDisplay(null); - } else { - if (messageNumber !== null) { - setSelectedMessageForDocDisplay( - messageNumber - ); - } else { - setSelectedMessageForDocDisplay(-1); - } + return ( +
+ { - if ( - previousMessage && - previousMessage.messageId - ) { + onEdit={(editedContent) => { + const parentMessageId = + message.parentMessageId!; + const parentMessage = + messageMap.get(parentMessageId)!; + upsertToCompleteMessageMap({ + messages: [ + { + ...parentMessage, + latestChildMessageId: null, + }, + ], + }); onSubmit({ messageIdToResend: - previousMessage.messageId, - forceSearch: true, - alternativeAssistantOverride: - currentAlternativeAssistant, + message.messageId || undefined, + messageOverride: editedContent, }); - } else { - setPopup({ - type: "error", - message: - "Failed to force search - please refresh the page and try again.", + }} + onMessageSelection={(messageId) => { + const newCompleteMessageMap = new Map( + messageMap + ); + newCompleteMessageMap.get( + message.parentMessageId! + )!.latestChildMessageId = messageId; + setCompleteMessageDetail({ + sessionId: + completeMessageDetail.sessionId, + messageMap: newCompleteMessageMap, }); - } - }} - retrievalDisabled={ - currentAlternativeAssistant - ? !personaIncludesRetrieval( - currentAlternativeAssistant! - ) - : !retrievalEnabled + setSelectedMessageForDocDisplay( + messageId + ); + // set message as latest so we can edit this message + // and so it sticks around on page reload + setMessageAsLatest(messageId); + }} + /> +
+ ); + } else if (message.type === "assistant") { + const isShowingRetrieved = + (selectedMessageForDocDisplay !== null && + selectedMessageForDocDisplay === + message.messageId) || + (selectedMessageForDocDisplay === + TEMP_USER_MESSAGE_ID && + i === messageHistory.length - 1); + const previousMessage = + i !== 0 ? messageHistory[i - 1] : null; + + const currentAlternativeAssistant = + message.alternateAssistantID != null + ? availableAssistants.find( + (persona) => + persona.id == + message.alternateAssistantID + ) + : null; + + return ( +
-
- ); - } else { - return ( -
+ > + 0) === true + } + handleFeedback={ + i === messageHistory.length - 1 && + isStreaming + ? undefined + : (feedbackType) => + setCurrentFeedback([ + feedbackType, + message.messageId as number, + ]) + } + handleSearchQueryEdit={ + i === messageHistory.length - 1 && + !isStreaming + ? (newQuery) => { + if (!previousMessage) { + setPopup({ + type: "error", + message: + "Cannot edit query of first message - please refresh the page and try again.", + }); + return; + } + + if ( + previousMessage.messageId === + null + ) { + setPopup({ + type: "error", + message: + "Cannot edit query of a pending message - please wait a few seconds and try again.", + }); + return; + } + onSubmit({ + messageIdToResend: + previousMessage.messageId, + queryOverride: newQuery, + alternativeAssistantOverride: + currentAlternativeAssistant, + }); + } + : undefined + } + isCurrentlyShowingRetrieved={ + isShowingRetrieved + } + handleShowRetrieved={(messageNumber) => { + if (isShowingRetrieved) { + setSelectedMessageForDocDisplay(null); + } else { + if (messageNumber !== null) { + setSelectedMessageForDocDisplay( + messageNumber + ); + } else { + setSelectedMessageForDocDisplay(-1); + } + } + }} + handleForceSearch={() => { + if ( + previousMessage && + previousMessage.messageId + ) { + onSubmit({ + messageIdToResend: + previousMessage.messageId, + forceSearch: true, + alternativeAssistantOverride: + currentAlternativeAssistant, + }); + } else { + setPopup({ + type: "error", + message: + "Failed to force search - please refresh the page and try again.", + }); + } + }} + retrievalDisabled={ + currentAlternativeAssistant + ? !personaIncludesRetrieval( + currentAlternativeAssistant! + ) + : !retrievalEnabled + } + /> +
+ ); + } else { + return ( +
+ + {message.message} +

+ } + /> +
+ ); + } + })} + {isStreaming && + messageHistory.length > 0 && + messageHistory[messageHistory.length - 1].type === + "user" && ( +
- {message.message} -

+
+ +
} />
- ); - } - })} - {isStreaming && - messageHistory.length > 0 && - messageHistory[messageHistory.length - 1].type === - "user" && ( -
- - -
- } - /> -
- )} + )} - {/* Some padding at the bottom so the search bar has space at the bottom to not cover the last message*/} -
-
- - {currentPersona && - currentPersona.starter_messages && - currentPersona.starter_messages.length > 0 && - selectedAssistant && - messageHistory.length === 0 && - !isFetchingChatMessages && ( -
0 && + selectedAssistant && + messageHistory.length === 0 && + !isFetchingChatMessages && ( +
- {currentPersona.starter_messages.map( - (starterMessage, i) => ( -
- - onSubmit({ - messageOverride: - starterMessage.message, - }) - } - /> -
- ) - )} + > + {currentPersona.starter_messages.map( + (starterMessage, i) => ( +
+ + onSubmit({ + messageOverride: + starterMessage.message, + }) + } + /> +
+ ) + )} +
+ )} + {/* Some padding at the bottom so the search bar has space at the bottom to not cover the last message*/} +
+
+
+
+
+
+ {aboveHorizon && ( +
+
)} -
-
-
-
-
- {aboveHorizon && ( -
- -
- )} - setDocumentSelection(true)} - selectedDocuments={selectedDocuments} - // assistant stuff - assistantOptions={filteredAssistants} - selectedAssistant={liveAssistant} - setSelectedAssistant={onAssistantChange} - setAlternativeAssistant={setAlternativeAssistant} - alternativeAssistant={alternativeAssistant} - // end assistant stuff - message={message} - setMessage={setMessage} - onSubmit={onSubmit} - isStreaming={isStreaming} - setIsCancelled={setIsCancelled} - filterManager={filterManager} - llmOverrideManager={llmOverrideManager} - files={currentMessageFiles} - setFiles={setCurrentMessageFiles} - handleFileUpload={handleImageUpload} - textAreaRef={textAreaRef} - chatSessionId={chatSessionIdRef.current!} - /> + setDocumentSelection(true)} + selectedDocuments={selectedDocuments} + // assistant stuff + assistantOptions={filteredAssistants} + selectedAssistant={liveAssistant} + setSelectedAssistant={onAssistantChange} + setAlternativeAssistant={setAlternativeAssistant} + alternativeAssistant={alternativeAssistant} + // end assistant stuff + message={message} + setMessage={setMessage} + onSubmit={onSubmit} + isStreaming={isStreaming} + setIsCancelled={setIsCancelled} + filterManager={filterManager} + llmOverrideManager={llmOverrideManager} + files={currentMessageFiles} + setFiles={setCurrentMessageFiles} + handleFileUpload={handleImageUpload} + textAreaRef={textAreaRef} + chatSessionId={chatSessionIdRef.current!} + /> +
-
- )} - - ) : ( -
-
+ ) : ( +
+
-
- + /> +
+ +
-
- )} + )} +
- - setDocumentSelection(false)} - selectedMessage={aiMessage} - selectedDocuments={selectedDocuments} - toggleDocumentSelection={toggleDocumentSelection} - clearSelectedDocuments={clearSelectedDocuments} - selectedDocumentTokens={selectedDocumentTokens} - maxTokens={maxTokens} - isLoading={isFetchingChatMessages} - isOpen={documentSelection} - /> +
-
+ setDocumentSelection(false)} + selectedMessage={aiMessage} + selectedDocuments={selectedDocuments} + toggleDocumentSelection={toggleDocumentSelection} + clearSelectedDocuments={clearSelectedDocuments} + selectedDocumentTokens={selectedDocumentTokens} + maxTokens={maxTokens} + isLoading={isFetchingChatMessages} + isOpen={documentSelection} + /> ); } diff --git a/web/src/app/chat/documentSidebar/DocumentSidebar.tsx b/web/src/app/chat/documentSidebar/DocumentSidebar.tsx index 17a239b30aa5..2a27fd97f0ab 100644 --- a/web/src/app/chat/documentSidebar/DocumentSidebar.tsx +++ b/web/src/app/chat/documentSidebar/DocumentSidebar.tsx @@ -2,44 +2,10 @@ import { DanswerDocument } from "@/lib/search/interfaces"; import { Divider, Text } from "@tremor/react"; import { ChatDocumentDisplay } from "./ChatDocumentDisplay"; import { usePopup } from "@/components/admin/connectors/Popup"; -import { FiAlertTriangle, FiFileText } from "react-icons/fi"; -import { SelectedDocumentDisplay } from "./SelectedDocumentDisplay"; import { removeDuplicateDocs } from "@/lib/documentUtils"; -import { BasicSelectable } from "@/components/BasicClickable"; import { Message, RetrievalType } from "../interfaces"; -import { SIDEBAR_WIDTH } from "@/lib/constants"; -import { HoverPopup } from "@/components/HoverPopup"; -import { TbLayoutSidebarLeftExpand } from "react-icons/tb"; import { ForwardedRef, forwardRef } from "react"; -function SectionHeader({ - name, - icon, - closeHeader, -}: { - name: string; - icon: React.FC<{ className: string }>; - closeHeader?: () => void; -}) { - return ( -
-
-

- {icon({ className: "my-auto mr-1" })} - {name} -

- {closeHeader && ( - - )} -
-
- ); -} - interface DocumentSidebarProps { closeSidebar: () => void; selectedMessage: Message | null; @@ -71,8 +37,6 @@ export const DocumentSidebar = forwardRef( ) => { const { popup, setPopup } = usePopup(); - const selectedMessageRetrievalType = selectedMessage?.retrievalType || null; - const selectedDocumentIds = selectedDocuments?.map((document) => document.document_id) || []; @@ -88,9 +52,7 @@ export const DocumentSidebar = forwardRef( return (
{ if (e.target === e.currentTarget) { closeSidebar(); diff --git a/web/src/app/chat/input/ChatInputBar.tsx b/web/src/app/chat/input/ChatInputBar.tsx index c7885a345ebb..56c15cf5ce34 100644 --- a/web/src/app/chat/input/ChatInputBar.tsx +++ b/web/src/app/chat/input/ChatInputBar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { FiPlusCircle, FiPlus, FiInfo, FiX } from "react-icons/fi"; import { ChatInputOption } from "./ChatInputOption"; import { Persona } from "@/app/admin/assistants/interfaces"; @@ -29,6 +29,7 @@ import { DanswerDocument } from "@/lib/search/interfaces"; import { AssistantIcon } from "@/components/assistants/AssistantIcon"; import { Tooltip } from "@/components/tooltip/Tooltip"; import { Hoverable } from "@/components/Hoverable"; +import { SettingsContext } from "@/components/settings/SettingsProvider"; const MAX_INPUT_HEIGHT = 200; export function ChatInputBar({ @@ -103,6 +104,7 @@ export function ChatInputBar({ } } }; + const settings = useContext(SettingsContext); const { llmProviders } = useChatContext(); const [_, llmName] = getFinalLLM(llmProviders, selectedAssistant, null); @@ -213,9 +215,8 @@ export function ChatInputBar({ className=" w-[90%] shrink - bg-background relative - px-4 + desktop:px-4 max-w-searchbar-max mx-auto " @@ -390,7 +391,7 @@ export function ChatInputBar({ style={{ scrollbarWidth: "thin" }} role="textarea" aria-multiline - placeholder="Send a message or @ to tag an assistant..." + placeholder={`Send a message ${!settings?.isMobile ? "or @ to tag an assistant..." : ""}`} value={message} onKeyDown={(event) => { if ( @@ -420,7 +421,9 @@ export function ChatInputBar({ }} /> )} + flexPriority="shrink" position="top" + mobilePosition="top-right" > )} position="top" + // flexPriority="second" > @@ -484,7 +492,7 @@ export function ChatInputBar({ }} />
-
+
{ diff --git a/web/src/app/chat/input/ChatInputOption.tsx b/web/src/app/chat/input/ChatInputOption.tsx index 69ed373e5662..804dc4749e85 100644 --- a/web/src/app/chat/input/ChatInputOption.tsx +++ b/web/src/app/chat/input/ChatInputOption.tsx @@ -5,7 +5,7 @@ import { Popover } from "../../../components/popover/Popover"; import { IconProps } from "@/components/icons/icons"; interface ChatInputOptionProps { - name: string; + name?: string; Icon: ({ size, className }: IconProps) => JSX.Element; onClick?: () => void; size?: number; @@ -61,7 +61,7 @@ export const ChatInputOption: React.FC = ({ rounded-md ${ flexPriority === "shrink" && - "flex-shrink-[10000] flex-grow-0 flex-basis-auto min-w-[30px] whitespace-nowrap overflow-hidden" + "flex-shrink-100 flex-grow-0 flex-basis-auto min-w-[30px] whitespace-nowrap overflow-hidden" } ${ flexPriority === "second" && @@ -76,7 +76,7 @@ export const ChatInputOption: React.FC = ({ onClick={onClick} > - {name} + {name && {name}} {isTooltipVisible && tooltipContent && (
; distance: number; debounce: number; + mobile?: boolean; }) { const preventScrollInterference = useRef(false); const preventScroll = useRef(false); diff --git a/web/src/app/chat/message/Messages.tsx b/web/src/app/chat/message/Messages.tsx index 3d99413939b1..5dfc0d259701 100644 --- a/web/src/app/chat/message/Messages.tsx +++ b/web/src/app/chat/message/Messages.tsx @@ -13,7 +13,7 @@ import { FiGlobe, } from "react-icons/fi"; import { FeedbackType } from "../types"; -import { useEffect, useRef, useState } from "react"; +import { useContext, useEffect, useRef, useState } from "react"; import ReactMarkdown from "react-markdown"; import { DanswerDocument, @@ -58,6 +58,7 @@ import { ValidSources } from "@/lib/types"; import { Tooltip } from "@/components/tooltip/Tooltip"; import { useMouseTracking } from "./hooks"; import { InternetSearchIcon } from "@/components/InternetSearchIcon"; +import { SettingsContext } from "@/components/settings/SettingsProvider"; const TOOLS_WITH_CUSTOM_HANDLING = [ SEARCH_TOOL_NAME, @@ -162,6 +163,7 @@ export const AIMessage = ({ const { isHovering, trackedElementRef, hoverElementRef } = useMouseTracking(); + const settings = useContext(SettingsContext); // this is needed to give Prism a chance to load if (!isReady) { return
; @@ -226,7 +228,7 @@ export const AIMessage = ({ return (
-
+
- {filteredDocs.length > 0 && + {!settings?.isMobile && + filteredDocs.length > 0 && filteredDocs.slice(0, 2).map((doc, ind) => (
- +
- The AI decided this query didn't need a search + + The AI decided this query didn't need a search + + No search
diff --git a/web/src/app/chat/page.tsx b/web/src/app/chat/page.tsx index 5371816000de..bdd5de2479fd 100644 --- a/web/src/app/chat/page.tsx +++ b/web/src/app/chat/page.tsx @@ -43,15 +43,14 @@ export default async function Page({ return ( <> - - {shouldShowWelcomeModal && } + {/* */} + {/* {shouldShowWelcomeModal && } {!shouldShowWelcomeModal && !shouldDisplaySourcesIncompleteModal && ( )} {shouldDisplaySourcesIncompleteModal && ( - )} - + )} */} void; }) { const router = useRouter(); const [isDeletionModalVisible, setIsDeletionModalVisible] = useState(false); @@ -62,6 +65,7 @@ export function ChatSessionDisplay({ alert("Failed to rename chat session"); } }; + const settings = useContext(SettingsContext); return ( <> @@ -92,6 +96,11 @@ export function ChatSessionDisplay({ { + if (settings?.isMobile && closeSidebar) { + closeSidebar(); + } + }} href={ search ? `/search?searchId=${chatSession.id}` diff --git a/web/src/app/chat/sessionSidebar/HistorySidebar.tsx b/web/src/app/chat/sessionSidebar/HistorySidebar.tsx index 9b70a9301b20..4a5c54afa7ba 100644 --- a/web/src/app/chat/sessionSidebar/HistorySidebar.tsx +++ b/web/src/app/chat/sessionSidebar/HistorySidebar.tsx @@ -48,6 +48,7 @@ interface HistorySidebarProps { openedFolders?: { [key: number]: boolean }; toggleSidebar?: () => void; toggled?: boolean; + removeToggle?: () => void; } export const HistorySidebar = forwardRef( @@ -60,6 +61,7 @@ export const HistorySidebar = forwardRef( folders, openedFolders, toggleSidebar, + removeToggle, }, ref: ForwardedRef ) => { @@ -101,11 +103,11 @@ export const HistorySidebar = forwardRef( transition-transform`} >
-
+
-
+
{enterpriseSettings && enterpriseSettings.application_name ? (
@@ -119,13 +121,18 @@ export const HistorySidebar = forwardRef( Danswer )}
+ {toggleSidebar && ( )} @@ -182,6 +189,7 @@ export const HistorySidebar = forwardRef(
void; }) { const groupedChatSessions = existingChats ? groupSessionsByDateRange(existingChats) @@ -58,7 +60,7 @@ export function PagesTab({ const isHistoryEmpty = !existingChats || existingChats.length === 0; return ( -
+
{folders && folders.length > 0 && (
@@ -115,6 +117,7 @@ export function PagesTab({ return (
-
-
+
+
@@ -33,7 +33,7 @@ export default function FixedLogo() {
-
+
diff --git a/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx b/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx index 0202bbdcff71..8d1922a3aad8 100644 --- a/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx +++ b/web/src/app/chat/shared_chat_search/FunctionalWrapper.tsx @@ -10,6 +10,7 @@ const ToggleSwitch = () => { const commandSymbol = KeyboardSymbol(); const pathname = usePathname(); const router = useRouter(); + const settings = useContext(SettingsContext); const [activeTab, setActiveTab] = useState(() => { return pathname == "/search" ? "search" : "chat"; @@ -27,13 +28,17 @@ const ToggleSwitch = () => { const handleTabChange = (tab: string) => { setActiveTab(tab); localStorage.setItem("activeTab", tab); - router.push(tab === "search" ? "/search" : "/chat"); + if (settings?.isMobile) { + window.location.href = tab; + } else { + router.push(tab === "search" ? "/search" : "/chat"); + } }; return ( -
+
{