diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index df7ac4aec7d6..ebcf346802a0 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -170,13 +170,7 @@ module.exports = { "@typescript-eslint/no-unnecessary-type-arguments": "error", "@typescript-eslint/prefer-string-starts-ends-with": "error", "@typescript-eslint/switch-exhaustiveness-check": "error", - "@typescript-eslint/restrict-template-expressions": [ - "error", - { - allowNumber: true, - allowBoolean: true, - }, - ], + "@typescript-eslint/restrict-template-expressions": "off", }, }, ], diff --git a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx index 55fb7d5e88c8..22dd6bd6d82d 100644 --- a/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx +++ b/frontend/app/chat/[chatId]/components/ActionsBar/components/ChatInput/index.tsx @@ -1,7 +1,7 @@ "use client"; -import { useTranslation } from "react-i18next"; -import Button from "@/lib/components/ui/Button"; +import Icon from "@/lib/components/ui/Icon/Icon"; +import { LoaderIcon } from "@/lib/components/ui/LoaderIcon/LoaderIcon"; import { OnboardingQuestions } from "./components"; import { ChatEditor } from "./components/ChatEditor/ChatEditor"; @@ -11,7 +11,11 @@ export const ChatInput = (): JSX.Element => { const { setMessage, submitQuestion, generatingAnswer, message } = useChatInput(); - const { t } = useTranslation(["chat"]); + const handleSubmitQuestion = () => { + if (message.trim() !== "") { + submitQuestion(); + } + }; return ( <> @@ -21,7 +25,7 @@ export const ChatInput = (): JSX.Element => { data-testid="chat-input-form" onSubmit={(e) => { e.preventDefault(); - submitQuestion(); + handleSubmitQuestion(); }} className="sticky bottom-0 bg-white dark:bg-black w-full flex items-center gap-2 z-20 p-2" > @@ -29,22 +33,21 @@ export const ChatInput = (): JSX.Element => { - -
- -
+ {generatingAnswer ? ( + + ) : ( + + )} diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/QADisplay.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/QADisplay.tsx index c4f2f4ac2f8b..2079c87f0170 100644 --- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/QADisplay.tsx +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/QADisplay.tsx @@ -23,7 +23,6 @@ export const QADisplay = ({ content }: QADisplayProps): JSX.Element => { speaker={"user"} text={user_message} promptName={prompt_title} - brainName={brain_name} metadata={metadata} // eslint-disable-line @typescript-eslint/no-unsafe-assignment /> ) => { - const { - containerClasses, - containerWrapperClasses, - handleCopy, - isCopied, - isUserSpeaker, - markdownClasses, - } = useMessageRow({ + const { handleCopy, isCopied, isUserSpeaker } = useMessageRow({ speaker, text, }); @@ -37,28 +33,32 @@ export const MessageRow = React.forwardRef( const messageContent = text ?? ""; return ( -
-
-
- {/* Left section for the question and prompt */} -
+
+ {!isUserSpeaker ? ( +
+
- {/* Right section for buttons */} -
- {!isUserSpeaker && ( - <> - - - )} +
+
+ ) : ( +
+ + Me +
+ )} + {} +
{children ?? ( - + )}
diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/CopyButton.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/CopyButton.tsx index 2810d0072511..1b4f843c15f7 100644 --- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/CopyButton.tsx +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/CopyButton.tsx @@ -1,4 +1,4 @@ -import { FaCheckCircle, FaCopy } from "react-icons/fa"; +import Icon from "@/lib/components/ui/Icon/Icon"; type CopyButtonProps = { handleCopy: () => void; @@ -14,6 +14,11 @@ export const CopyButton = ({ onClick={handleCopy} title={isCopied ? "Copied!" : "Copy to clipboard"} > - {isCopied ? : } + ); diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent/MessageContent.module.scss b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent/MessageContent.module.scss new file mode 100644 index 000000000000..30ab6d5645e5 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent/MessageContent.module.scss @@ -0,0 +1,68 @@ +@use "@/styles/Colors.module.scss"; +@use "@/styles/Spacings.module.scss"; +@use "@/styles/Typography.module.scss"; + +.markdown { + p { + margin: 0; + padding: 0; + } + + ul { + list-style-type: disc; + margin-top: 0; + padding: 0; + margin-left: Spacings.$spacing05; + } + + ol { + list-style-type: decimal; + padding-left: Spacings.$spacing05; + list-style-position: outside; + + li { + white-space-collapse: collapse; + } + } + + h1 { + @include Typography.H1; + } + + h2 { + @include Typography.H2; + } + + h3 { + @include Typography.H3; + } +} + +.user { + color: Colors.$black; +} + +.brain { + color: Colors.$white; +} + +.thinking { + animation: pulse 1s infinite; + font-size: 2px; + line-height: 16px; + width: 16px; + display: flex; + justify-content: center; +} + +@keyframes pulse { + 0% { + font-size: 6px; + } + 50% { + font-size: 16px; + } + 100% { + font-size: 6px; + } +} diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent/MessageContent.tsx similarity index 75% rename from frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent.tsx rename to frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent/MessageContent.tsx index 232736c542c2..749075384c24 100644 --- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent.tsx +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/MessageContent/MessageContent.tsx @@ -1,12 +1,14 @@ import { useEffect, useState } from "react"; import ReactMarkdown from "react-markdown"; +import styles from "./MessageContent.module.scss"; + export const MessageContent = ({ text, - markdownClasses, + isUser, }: { text: string; - markdownClasses: string; + isUser: boolean; }): JSX.Element => { const [showLog] = useState(true); const [isLog, setIsLog] = useState(true); @@ -39,13 +41,19 @@ export const MessageContent = ({ const { logs, cleanedText } = extractLog(text); return ( -
+
{isLog && showLog && logs.length > 0 && ( -
+
{logs}
)} - + {cleanedText}
diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain/QuestionBrain.module.scss b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain/QuestionBrain.module.scss new file mode 100644 index 000000000000..cc815cada395 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain/QuestionBrain.module.scss @@ -0,0 +1,9 @@ +@use "@/styles/Colors.module.scss"; +@use "@/styles/Spacings.module.scss"; + +.brain_name_wrapper { + display: flex; + align-items: center; + gap: Spacings.$spacing02; + color: Colors.$black; +} diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain/QuestionBrain.tsx similarity index 51% rename from frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain.tsx rename to frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain/QuestionBrain.tsx index e8ed11b2d30a..cc3b5b1d3bf4 100644 --- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain.tsx +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionBrain/QuestionBrain.tsx @@ -1,5 +1,9 @@ import { Fragment } from "react"; +import Icon from "@/lib/components/ui/Icon/Icon"; + +import styles from "./QuestionBrain.module.scss"; + type QuestionBrainProps = { brainName?: string | null; }; @@ -11,11 +15,9 @@ export const QuestionBrain = ({ } return ( - - @{brainName} - +
+ + {brainName} +
); }; diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt/QuestionPompt.module.scss b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt/QuestionPompt.module.scss new file mode 100644 index 000000000000..2903ec0411f5 --- /dev/null +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt/QuestionPompt.module.scss @@ -0,0 +1,8 @@ +@use "@/styles/Colors.module.scss"; +@use "@/styles/Spacings.module.scss"; + +.prompt_name_wrapper { + display: flex; + align-items: center; + color: Colors.$black; +} diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt/QuestionPrompt.tsx similarity index 51% rename from frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt.tsx rename to frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt/QuestionPrompt.tsx index ccc6d5776c88..29699cdaf4bc 100644 --- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt.tsx +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/QuestionPrompt/QuestionPrompt.tsx @@ -1,5 +1,9 @@ import { Fragment } from "react"; +import Icon from "@/lib/components/ui/Icon/Icon"; + +import styles from "./QuestionPompt.module.scss"; + type QuestionProptProps = { promptName?: string | null; }; @@ -11,11 +15,9 @@ export const QuestionPrompt = ({ } return ( - - #{promptName} - +
+ + {promptName} +
); }; diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/SourcesButton.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/SourcesButton.tsx deleted file mode 100644 index 54146151d1ef..000000000000 --- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/components/SourcesButton.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import ReactDOM from "react-dom"; -import { FaQuestionCircle } from "react-icons/fa"; - -import { useEventTracking } from "@/services/analytics/june/useEventTracking"; - -type SourcesButtonProps = { - sources: [string] | []; -}; - -export const SourcesButton = ({ sources }: SourcesButtonProps): JSX.Element => { - const [showSources, setShowSources] = useState(false); - const [popupPosition, setPopupPosition] = useState({ top: 0, left: 0 }); - const { track } = useEventTracking(); - // Specify the type of element the ref will be attached to - const buttonRef = useRef(null); - - const updatePopupPosition = () => { - // Use the 'current' property of the ref with the correct type - if (buttonRef.current) { - const rect = buttonRef.current.getBoundingClientRect(); - setPopupPosition({ - top: rect.bottom + window.scrollY, - left: rect.left + window.scrollX, - }); - } - }; - - useEffect(() => { - window.addEventListener("scroll", updatePopupPosition); - - // Remove the event listener when the component is unmounted - return () => { - window.removeEventListener("scroll", updatePopupPosition); - }; - }, []); - - const sourcesList = ( -
    - {sources.map((source, index) => ( -
  • - {source.trim()} -
  • - ))} -
- ); - - return ( -
- - {showSources && - ReactDOM.createPortal( -
- {/* Display the sources list here */} - {sourcesList} -
, - document.body // Render the popup to the body element - )} -
- ); -}; diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/hooks/useMessageRow.ts b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/hooks/useMessageRow.ts index e7dcb907b5c2..1c6e34c02bfb 100644 --- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/hooks/useMessageRow.ts +++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/components/QADisplay/components/MessageRow/hooks/useMessageRow.ts @@ -1,7 +1,5 @@ import { useState } from "react"; -import { cn } from "@/lib/utils"; - type UseMessageRowProps = { speaker: "user" | "assistant"; text?: string; @@ -23,25 +21,9 @@ export const useMessageRow = ({ speaker, text }: UseMessageRowProps) => { setTimeout(() => setIsCopied(false), 2000); // Reset after 2 seconds }; - const containerClasses = cn( - "py-3 px-5 w-fit", - isUserSpeaker ? "bg-msg-gray bg-opacity-10" : "bg-msg-purple bg-opacity-40", - "dark:bg-gray-800 rounded-3xl flex flex-col overflow-hidden scroll-pb-32" - ); - - const containerWrapperClasses = cn( - "flex flex-col", - isUserSpeaker ? "items-end" : "items-start" - ); - - const markdownClasses = cn("prose", "dark:prose-invert"); - return { isUserSpeaker, isCopied, handleCopy, - containerClasses, - containerWrapperClasses, - markdownClasses, }; }; diff --git a/frontend/app/chat/[chatId]/utils/generatePlaceHolderMessage.ts b/frontend/app/chat/[chatId]/utils/generatePlaceHolderMessage.ts index 955ecad079e8..601ade7d0974 100644 --- a/frontend/app/chat/[chatId]/utils/generatePlaceHolderMessage.ts +++ b/frontend/app/chat/[chatId]/utils/generatePlaceHolderMessage.ts @@ -11,8 +11,10 @@ export const generatePlaceHolderMessage = ({ }: GeneratePlaceHolderMessageProps): ChatMessage => { return { message_id: new Date().getTime().toString(), - message_time: new Date(new Date().setDate(new Date().getDate() + 1)).toISOString(), - assistant: 'Thinking..', + message_time: new Date( + new Date().setDate(new Date().getDate() + 1) + ).toISOString(), + assistant: "🧠", chat_id, user_message, }; diff --git a/frontend/app/search/page.module.scss b/frontend/app/search/page.module.scss index 2fdef462a2b4..f315552ea1c7 100644 --- a/frontend/app/search/page.module.scss +++ b/frontend/app/search/page.module.scss @@ -34,7 +34,7 @@ align-items: center; .quivr_text { - @include Typography.H1; + @include Typography.Big; .quivr_text_primary { color: Colors.$primary; diff --git a/frontend/lib/components/ui/Icon/Icon.module.scss b/frontend/lib/components/ui/Icon/Icon.module.scss index 46cd8a093488..5ea013ec6aa2 100644 --- a/frontend/lib/components/ui/Icon/Icon.module.scss +++ b/frontend/lib/components/ui/Icon/Icon.module.scss @@ -29,6 +29,10 @@ } } +.dark-grey { + color: Colors.$dark-grey; +} + .primary { color: Colors.$primary; } @@ -42,5 +46,11 @@ } .disabled { + color: Colors.$black; + pointer-events: none; opacity: 0.2; } + +.hovered { + cursor: pointer; +} diff --git a/frontend/lib/helpers/iconList.ts b/frontend/lib/helpers/iconList.ts index 0a04ccfbc054..5a5228a5c8a6 100644 --- a/frontend/lib/helpers/iconList.ts +++ b/frontend/lib/helpers/iconList.ts @@ -1,4 +1,6 @@ import { AiOutlineLoading3Quarters } from "react-icons/ai"; +import { FaCheckCircle, FaRegUserCircle } from "react-icons/fa"; +import { FaArrowUpFromBracket } from "react-icons/fa6"; import { IconType } from "react-icons/lib"; import { LuBrain, @@ -8,13 +10,18 @@ import { LuPlusCircle, LuSearch, } from "react-icons/lu"; +import { RiHashtag } from "react-icons/ri"; export const iconList: { [name: string]: IconType } = { add: LuPlusCircle, brain: LuBrain, + checkCircle: FaCheckCircle, chevronDown: LuChevronDown, chevronRight: LuChevronRight, copy: LuCopy, + followUp: FaArrowUpFromBracket, + hastag: RiHashtag, loader: AiOutlineLoading3Quarters, search: LuSearch, + user: FaRegUserCircle, }; diff --git a/frontend/lib/types/Colors.ts b/frontend/lib/types/Colors.ts index 6afa9fc0a093..b6108d5c2fa2 100644 --- a/frontend/lib/types/Colors.ts +++ b/frontend/lib/types/Colors.ts @@ -1 +1 @@ -export type Color = "black" | "primary" | "accent" | "white"; +export type Color = "black" | "dark-grey" | "primary" | "accent" | "white"; diff --git a/frontend/styles/_Colors.module.scss b/frontend/styles/_Colors.module.scss index 9ebfd04bfe09..443d164f33f6 100644 --- a/frontend/styles/_Colors.module.scss +++ b/frontend/styles/_Colors.module.scss @@ -3,6 +3,7 @@ $lightest-grey: #f5f5f5; $light-grey: #d3d3d3; $normal-grey: #c8c8c8; $dark-grey: #707070; +$light-black: #415065; $black: #11243e; $dark-black: #081621; $primary: #6142d4; diff --git a/frontend/styles/_Typography.module.scss b/frontend/styles/_Typography.module.scss index 72ab2abef0ea..e90da26d814e 100644 --- a/frontend/styles/_Typography.module.scss +++ b/frontend/styles/_Typography.module.scss @@ -1,4 +1,19 @@ +@mixin Big { + font-weight: 500; + font-size: 36px; +} + @mixin H1 { - font-weight: 500; - font-size: 36px; -} \ No newline at end of file + font-weight: 600; + font-size: 20px; +} + +@mixin H2 { + font-weight: 600; + font-size: 18px; +} + +@mixin H3 { + font-weight: 500; + font-size: 16px; +}