diff --git a/frontend/app/chat/[chatId]/components/DataPanel/DataPanel.module.scss b/frontend/app/chat/[chatId]/components/DataPanel/DataPanel.module.scss
new file mode 100644
index 000000000000..1032bdff878f
--- /dev/null
+++ b/frontend/app/chat/[chatId]/components/DataPanel/DataPanel.module.scss
@@ -0,0 +1,7 @@
+@use "@/styles/Spacings.module.scss";
+
+.data_panel_wrapper {
+ display: flex;
+ flex-direction: column;
+ row-gap: Spacings.$spacing05;
+}
diff --git a/frontend/app/chat/[chatId]/components/DataPanel/DataPanel.tsx b/frontend/app/chat/[chatId]/components/DataPanel/DataPanel.tsx
new file mode 100644
index 000000000000..2408f0c76452
--- /dev/null
+++ b/frontend/app/chat/[chatId]/components/DataPanel/DataPanel.tsx
@@ -0,0 +1,33 @@
+"use client";
+
+import { useEffect, useState } from "react";
+
+import { useChatContext } from "@/lib/context";
+import { CloseBrain } from "@/lib/types/MessageMetadata";
+
+import styles from "./DataPanel.module.scss";
+import RelatedBrains from "./components/RelatedBrains/RelatedBrains";
+
+const DataPanel = (): JSX.Element => {
+ const { messages } = useChatContext();
+ const [lastMessageRelatedBrain, setLastMessageRelatedBrain] = useState<
+ CloseBrain[]
+ >([]);
+
+ useEffect(() => {
+ if (messages.length > 0) {
+ const lastMessage = messages[messages.length - 1];
+ if (lastMessage?.metadata?.close_brains) {
+ setLastMessageRelatedBrain(lastMessage.metadata.close_brains);
+ }
+ }
+ }, [lastMessageRelatedBrain, messages]);
+
+ return (
+
+
+
+ );
+};
+
+export default DataPanel;
diff --git a/frontend/app/chat/[chatId]/components/DataPanel/components/RelatedBrains/RelatedBrains.module.scss b/frontend/app/chat/[chatId]/components/DataPanel/components/RelatedBrains/RelatedBrains.module.scss
new file mode 100644
index 000000000000..d897eb80be4e
--- /dev/null
+++ b/frontend/app/chat/[chatId]/components/DataPanel/components/RelatedBrains/RelatedBrains.module.scss
@@ -0,0 +1,50 @@
+@use "@/styles/Colors.module.scss";
+@use "@/styles/Spacings.module.scss";
+
+.close_brains_wrapper {
+ display: flex;
+ flex-direction: column;
+ padding-bottom: Spacings.$spacing03;
+ gap: Spacings.$spacing01;
+
+ .brain_line {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ padding-left: Spacings.$spacing03;
+ padding-right: Spacings.$spacing05;
+ overflow: hidden;
+
+ .left {
+ display: flex;
+ align-items: center;
+ gap: Spacings.$spacing03;
+
+ .copy_icon {
+ visibility: hidden;
+ }
+
+ .brain_name {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ &.current {
+ color: Colors.$primary;
+ }
+ }
+ }
+
+ .similarity_score {
+ cursor: help;
+ }
+
+ &:hover {
+ .left .copy_icon {
+ visibility: visible;
+ cursor: pointer;
+ }
+ }
+ }
+}
diff --git a/frontend/app/chat/[chatId]/components/DataPanel/components/RelatedBrains/RelatedBrains.tsx b/frontend/app/chat/[chatId]/components/DataPanel/components/RelatedBrains/RelatedBrains.tsx
new file mode 100644
index 000000000000..4d380f9ac4e5
--- /dev/null
+++ b/frontend/app/chat/[chatId]/components/DataPanel/components/RelatedBrains/RelatedBrains.tsx
@@ -0,0 +1,94 @@
+import { useEffect, useState } from "react";
+
+import { FoldableSection } from "@/lib/components/ui/FoldableSection/FoldableSection";
+import Icon from "@/lib/components/ui/Icon/Icon";
+import { useChatContext } from "@/lib/context";
+import { CloseBrain } from "@/lib/types/MessageMetadata";
+
+import styles from "./RelatedBrains.module.scss";
+
+interface RelatedBrainsProps {
+ closeBrains: CloseBrain[];
+}
+
+interface CloseBrainProps {
+ color: string;
+ isCurrentBrain: boolean;
+}
+
+const RelatedBrains = ({ closeBrains }: RelatedBrainsProps): JSX.Element => {
+ const [closeBrainsProps, setCloseBrainProps] = useState
(
+ []
+ );
+ const { messages } = useChatContext();
+ const lerp = (start: number, end: number, t: number): number => {
+ return start * (1 - t) + end * t;
+ };
+
+ useEffect(() => {
+ const newProps = closeBrains.map((brain) => {
+ const t = Math.pow(brain.similarity, 2);
+ const r = Math.round(lerp(211, 138, t));
+ const g = Math.round(lerp(211, 43, t));
+ const b = Math.round(lerp(211, 226, t));
+ const isCurrentBrain =
+ brain.name === messages[messages.length - 1].brain_name;
+
+ return { color: `rgb(${r}, ${g}, ${b})`, isCurrentBrain: isCurrentBrain };
+ });
+ setCloseBrainProps(newProps);
+ }, [closeBrains, messages]);
+
+ if (closeBrains.length === 0) {
+ return <>>;
+ }
+
+ return (
+
+
+ {closeBrains.map((brain, index) => (
+
+
+
+
+ void navigator.clipboard.writeText("@" + brain.name)
+ }
+ >
+
+
+ @{brain.name}
+
+
+
+ {Math.round(brain.similarity * 100)}
+
+
+ ))}
+
+
+ );
+};
+
+export default RelatedBrains;
diff --git a/frontend/app/chat/[chatId]/hooks/useChat.ts b/frontend/app/chat/[chatId]/hooks/useChat.ts
index f5c21e96d64c..9e4a3b56ab99 100644
--- a/frontend/app/chat/[chatId]/hooks/useChat.ts
+++ b/frontend/app/chat/[chatId]/hooks/useChat.ts
@@ -83,7 +83,7 @@ export const useChat = () => {
}
const chatQuestion: ChatQuestion = {
- model,
+ model, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
question,
temperature: temperature,
max_tokens: maxTokens,
@@ -93,7 +93,6 @@ export const useChat = () => {
callback?.();
await addStreamQuestion(currentChatId, chatQuestion);
-
} catch (error) {
console.error({ error });
diff --git a/frontend/app/chat/[chatId]/page.module.scss b/frontend/app/chat/[chatId]/page.module.scss
new file mode 100644
index 000000000000..7a0024595530
--- /dev/null
+++ b/frontend/app/chat/[chatId]/page.module.scss
@@ -0,0 +1,20 @@
+@use "@/styles/Colors.module.scss";
+@use "@/styles/Spacings.module.scss";
+
+.chat_page_container {
+ display: flex;
+ flex: 1 1 0%;
+ background-color: Colors.$white;
+ padding: Spacings.$spacing06;
+ display: flex;
+ gap: Spacings.$spacing09;
+
+ &.feeding {
+ background-color: Colors.$chat-bg-gray;
+ }
+
+ .data_panel_wrapper {
+ height: 100%;
+ width: 30%;
+ }
+}
diff --git a/frontend/app/chat/[chatId]/page.tsx b/frontend/app/chat/[chatId]/page.tsx
index d11fa5df66f7..9f6f01eafe24 100644
--- a/frontend/app/chat/[chatId]/page.tsx
+++ b/frontend/app/chat/[chatId]/page.tsx
@@ -1,41 +1,54 @@
"use client";
import { useKnowledgeToFeedContext } from "@/lib/context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
+import { useDevice } from "@/lib/hooks/useDevice";
import { useCustomDropzone } from "@/lib/hooks/useDropzone";
import { cn } from "@/lib/utils";
import { ActionsBar } from "./components/ActionsBar";
import { ChatDialogueArea } from "./components/ChatDialogueArea/ChatDialogue";
+import DataPanel from "./components/DataPanel/DataPanel";
import { useChatNotificationsSync } from "./hooks/useChatNotificationsSync";
+import styles from "./page.module.scss";
const SelectedChatPage = (): JSX.Element => {
- const { getRootProps } = useCustomDropzone();
- const { shouldDisplayFeedCard } = useKnowledgeToFeedContext();
+ const { getRootProps } = useCustomDropzone();
+ const { shouldDisplayFeedCard } = useKnowledgeToFeedContext();
+ const { isMobile } = useDevice();
- useChatNotificationsSync();
+ useChatNotificationsSync();
- return (
-
-
+ return (
+
+
+ {!isMobile && (
+
+
+
+ )}
+
+ );
};
export default SelectedChatPage;
diff --git a/frontend/app/chat/[chatId]/types/index.ts b/frontend/app/chat/[chatId]/types/index.ts
index a478c898af65..d6dad3b85619 100644
--- a/frontend/app/chat/[chatId]/types/index.ts
+++ b/frontend/app/chat/[chatId]/types/index.ts
@@ -1,5 +1,7 @@
import { UUID } from "crypto";
+import { CloseBrain } from "@/lib/types/MessageMetadata";
+
export type ChatQuestion = {
model?: string;
question?: string;
@@ -18,6 +20,7 @@ export type ChatMessage = {
brain_name?: string;
metadata?: {
sources?: [string];
+ close_brains?: CloseBrain[];
};
};
diff --git a/frontend/app/search/page.module.scss b/frontend/app/search/page.module.scss
index cdfd2ad99d72..2fdef462a2b4 100644
--- a/frontend/app/search/page.module.scss
+++ b/frontend/app/search/page.module.scss
@@ -6,7 +6,7 @@
@use "@/styles/Variables.module.scss";
.search_page_container {
- background-color: Colors.$ivory;
+ background-color: Colors.$white;
width: 100%;
height: 100%;
display: flex;
diff --git a/frontend/lib/components/Menu/Menu.tsx b/frontend/lib/components/Menu/Menu.tsx
index 14a996d673ae..c63793f21df8 100644
--- a/frontend/lib/components/Menu/Menu.tsx
+++ b/frontend/lib/components/Menu/Menu.tsx
@@ -5,7 +5,7 @@ import { MenuControlButton } from "@/app/chat/[chatId]/components/ActionsBar/com
import { nonProtectedPaths } from "@/lib/config/routesConfig";
import { useMenuContext } from "@/lib/context/MenuProvider/hooks/useMenuContext";
-import styles from './Menu.module.scss'
+import styles from "./Menu.module.scss";
import { AnimatedDiv } from "./components/AnimationDiv";
import { BrainsManagementButton } from "./components/BrainsManagementButton";
import { DiscussionButton } from "./components/DiscussionButton";
@@ -16,51 +16,58 @@ import { ProfileButton } from "./components/ProfileButton";
import { UpgradeToPlus } from "./components/UpgradeToPlus";
export const Menu = (): JSX.Element => {
- const { isOpened } = useMenuContext();
- const pathname = usePathname() ?? "";
+ const { isOpened } = useMenuContext();
+ const pathname = usePathname() ?? "";
- if (nonProtectedPaths.includes(pathname)) {
- return <>>;
- }
+ if (nonProtectedPaths.includes(pathname)) {
+ return <>>;
+ }
- const displayedOnPages = ["/chat", "/library", "/brains-management", "/search"];
+ const displayedOnPages = [
+ "/chat",
+ "/library",
+ "/brains-management",
+ "/search",
+ ];
- const isMenuDisplayed = displayedOnPages.some((page) =>
- pathname.includes(page)
- );
+ const isMenuDisplayed = displayedOnPages.some((page) =>
+ pathname.includes(page)
+ );
- if (!isMenuDisplayed) {
- return <>>;
- }
+ if (!isMenuDisplayed) {
+ return <>>;
+ }
- /* eslint-disable @typescript-eslint/restrict-template-expressions */
+ /* eslint-disable @typescript-eslint/restrict-template-expressions */
- return (
-
-
-
-
-
+ return (
+
+
+
+
+
+
+
+
+ );
};
diff --git a/frontend/lib/components/Menu/components/AnimationDiv.tsx b/frontend/lib/components/Menu/components/AnimationDiv.tsx
index 2d8cdbeaedbd..6d5cb89ac564 100644
--- a/frontend/lib/components/Menu/components/AnimationDiv.tsx
+++ b/frontend/lib/components/Menu/components/AnimationDiv.tsx
@@ -21,7 +21,7 @@ export const AnimatedDiv = ({ children }: AnimatedDivProps): JSX.Element => {
? "10px 10px 16px rgba(0, 0, 0, 0)"
: "10px 10px 16px rgba(0, 0, 0, 0.5)",
}}
- className={"overflow-hidden flex flex-col flex-1 bg-white"}
+ className={"overflow-hidden flex flex-col flex-1 bg-grey"}
>
{children}
diff --git a/frontend/lib/components/ui/FoldableSection/FoldableSection.module.scss b/frontend/lib/components/ui/FoldableSection/FoldableSection.module.scss
new file mode 100644
index 000000000000..c4180c3b3f00
--- /dev/null
+++ b/frontend/lib/components/ui/FoldableSection/FoldableSection.module.scss
@@ -0,0 +1,36 @@
+@use "@/styles/Colors.module.scss";
+@use "@/styles/Spacings.module.scss";
+
+.foldable_section_wrapper {
+ display: flex;
+ flex-direction: column;
+ border-radius: 5px;
+ border: 1px dashed Colors.$light-grey;
+ overflow: hidden;
+
+ .header_wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ cursor: pointer;
+ padding: Spacings.$spacing03;
+
+ .header_left {
+ display: flex;
+ align-items: center;
+ gap: Spacings.$spacing03;
+ overflow: hidden;
+
+ .header_title {
+ font-weight: bold;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ &:hover {
+ background-color: Colors.$lightest-grey;
+ }
+ }
+}
diff --git a/frontend/lib/components/ui/FoldableSection/FoldableSection.tsx b/frontend/lib/components/ui/FoldableSection/FoldableSection.tsx
new file mode 100644
index 000000000000..549fa226c9dc
--- /dev/null
+++ b/frontend/lib/components/ui/FoldableSection/FoldableSection.tsx
@@ -0,0 +1,39 @@
+import { useEffect, useState } from "react";
+
+import { iconList } from "@/lib/helpers/iconList";
+
+import styles from "./FoldableSection.module.scss";
+
+import { Icon } from "../Icon/Icon";
+
+interface FoldableSectionProps {
+ label: string;
+ icon: keyof typeof iconList;
+ children: React.ReactNode;
+ foldedByDefault?: boolean;
+}
+
+export const FoldableSection = (props: FoldableSectionProps): JSX.Element => {
+ const [folded, setFolded] = useState(false);
+
+ useEffect(() => {
+ setFolded(props.foldedByDefault ?? false);
+ }, [props.foldedByDefault]);
+
+ return (
+
+
setFolded(!folded)}>
+
+
+
+ {!folded &&
{props.children}
}
+
+ );
+};
diff --git a/frontend/lib/components/ui/Icon/Icon.module.scss b/frontend/lib/components/ui/Icon/Icon.module.scss
index 43e854d7d36c..46cd8a093488 100644
--- a/frontend/lib/components/ui/Icon/Icon.module.scss
+++ b/frontend/lib/components/ui/Icon/Icon.module.scss
@@ -25,7 +25,7 @@
color: Colors.$black;
&.hovered {
- color: Colors.$accent;
+ color: Colors.$primary;
}
}
diff --git a/frontend/lib/components/ui/Icon/Icon.tsx b/frontend/lib/components/ui/Icon/Icon.tsx
index e940965728ad..c4d7050ac89b 100644
--- a/frontend/lib/components/ui/Icon/Icon.tsx
+++ b/frontend/lib/components/ui/Icon/Icon.tsx
@@ -1,3 +1,4 @@
+import { useEffect, useState } from "react";
import { IconType } from "react-icons/lib";
import { iconList } from "@/lib/helpers/iconList";
@@ -13,6 +14,8 @@ interface IconProps {
disabled?: boolean;
classname?: string;
hovered?: boolean;
+ handleHover?: boolean;
+ onClick?: () => void;
}
export const Icon = ({
@@ -22,9 +25,18 @@ export const Icon = ({
disabled,
classname,
hovered,
+ handleHover,
+ onClick,
}: IconProps): JSX.Element => {
+ const [iconHovered, setIconHovered] = useState(false);
const IconComponent: IconType = iconList[name];
+ useEffect(() => {
+ if (!handleHover) {
+ setIconHovered(!!hovered);
+ }
+ }, [hovered, handleHover]);
+
return (
handleHover && setIconHovered(true)}
+ onMouseLeave={() => handleHover && setIconHovered(false)}
+ onClick={onClick}
/>
);
};
diff --git a/frontend/lib/components/ui/SearchBar/SearchBar.module.scss b/frontend/lib/components/ui/SearchBar/SearchBar.module.scss
index 1c81396b2b26..156065aaaea1 100644
--- a/frontend/lib/components/ui/SearchBar/SearchBar.module.scss
+++ b/frontend/lib/components/ui/SearchBar/SearchBar.module.scss
@@ -10,12 +10,7 @@
background-color: Colors.$white;
padding: Spacings.$spacing05;
border-radius: 12px;
- box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
-
- &:hover {
- box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1),
- 0 8px 10px -6px rgb(0 0 0 / 0.1);
- }
+ border: 1px solid Colors.$light-grey;
.search_icon {
width: IconSizes.$big;
diff --git a/frontend/lib/components/ui/TextButton/TextButton.module.scss b/frontend/lib/components/ui/TextButton/TextButton.module.scss
index 158a18935f43..c7535d0f38cf 100644
--- a/frontend/lib/components/ui/TextButton/TextButton.module.scss
+++ b/frontend/lib/components/ui/TextButton/TextButton.module.scss
@@ -13,6 +13,6 @@
color: Colors.$black;
&.hovered {
- color: Colors.$accent;
+ color: Colors.$primary;
}
}
diff --git a/frontend/lib/helpers/iconList.ts b/frontend/lib/helpers/iconList.ts
index 666288fad6df..0a04ccfbc054 100644
--- a/frontend/lib/helpers/iconList.ts
+++ b/frontend/lib/helpers/iconList.ts
@@ -1,9 +1,20 @@
import { AiOutlineLoading3Quarters } from "react-icons/ai";
import { IconType } from "react-icons/lib";
-import { LuPlusCircle, LuSearch } from "react-icons/lu";
+import {
+ LuBrain,
+ LuChevronDown,
+ LuChevronRight,
+ LuCopy,
+ LuPlusCircle,
+ LuSearch,
+} from "react-icons/lu";
export const iconList: { [name: string]: IconType } = {
add: LuPlusCircle,
+ brain: LuBrain,
+ chevronDown: LuChevronDown,
+ chevronRight: LuChevronRight,
+ copy: LuCopy,
loader: AiOutlineLoading3Quarters,
search: LuSearch,
};
diff --git a/frontend/lib/types/MessageMetadata.ts b/frontend/lib/types/MessageMetadata.ts
new file mode 100644
index 000000000000..550dafc4db0b
--- /dev/null
+++ b/frontend/lib/types/MessageMetadata.ts
@@ -0,0 +1,9 @@
+export interface CloseBrain {
+ id: string;
+ similarity: number;
+ name: string;
+}
+
+export interface MessageMetadata {
+ closeBrains: CloseBrain[];
+}
diff --git a/frontend/styles/_Colors.module.scss b/frontend/styles/_Colors.module.scss
index f2fa73dea507..df110d9bf791 100644
--- a/frontend/styles/_Colors.module.scss
+++ b/frontend/styles/_Colors.module.scss
@@ -1,8 +1,12 @@
-$white: #FFFFFF;
-$black: #11243E;
-$primary: #6142D4;
-$secondary: #F3ECFF;
-$tertiary: #F6F4FF;
-$accent: #13ABBA;
-$highlight: #FAFAFA;
-$ivory: #FCFAF6,
\ No newline at end of file
+$white: #ffffff;
+$lightest-grey: #f5f5f5;
+$light-grey: #d3d3d3;
+$normal-grey: #c8c8c8;
+$black: #11243e;
+$primary: #6142d4;
+$secondary: #f3ecff;
+$tertiary: #f6f4ff;
+$accent: #13abba;
+$highlight: #fafafa;
+$ivory: #fcfaf6;
+$chat-bg-gray: #d9d9d9;