diff --git a/client/package.json b/client/package.json
index 4fcf2f4a9f..5d9a60fd76 100644
--- a/client/package.json
+++ b/client/package.json
@@ -53,7 +53,7 @@
"filenamify": "^6.0.0",
"html2canvas": "^1.4.1",
"lodash": "^4.17.21",
- "lucide-react": "^0.113.0",
+ "lucide-react": "^0.220.0",
"pino": "^8.12.1",
"rc-input-number": "^7.4.2",
"react": "^18.2.0",
diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx
index 223804f5d9..613e282191 100644
--- a/client/src/components/Messages/Message.jsx
+++ b/client/src/components/Messages/Message.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useRef } from 'react';
+import { useState, useEffect, useRef } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import copy from 'copy-to-clipboard';
import SubRow from './Content/SubRow';
@@ -31,7 +31,7 @@ export default function Message({
siblingCount,
setSiblingIdx
}) {
- const { text, searchResult, isCreatedByUser, error, submitting, unfinished, cancelled } = message;
+ const { text, searchResult, isCreatedByUser, error, submitting, unfinished } = message;
const isSubmitting = useRecoilValue(store.isSubmitting);
const setLatestMessage = useSetRecoilState(store.latestMessage);
const [abortScroll, setAbort] = useState(false);
@@ -74,6 +74,7 @@ export default function Message({
};
const getError = (text) => {
+ const errorMessage = text.length > 512 ? text.slice(0, 512) + '...' : text;
const match = text.match(/\{[^{}]*\}/);
var json = match ? match[0] : '';
if (isJson(json)) {
@@ -83,10 +84,10 @@ export default function Message({
} else if (json.type === 'insufficient_quota') {
return "We're sorry, but the default API key has reached its limit. To continue using this service, please set up your own API key. You can do this by clicking on the model logo in the top-left corner of the textbox.";
} else {
- return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${text}`;
+ return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${errorMessage}`;
}
} else {
- return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${text}`;
+ return `Oops! Something went wrong. Please try again in a few moments. Here's the specific error message we encountered: ${errorMessage}`;
}
};
diff --git a/client/src/components/Messages/MessageHeader.jsx b/client/src/components/Messages/MessageHeader.jsx
index a907cefbe7..238ca6e01a 100644
--- a/client/src/components/Messages/MessageHeader.jsx
+++ b/client/src/components/Messages/MessageHeader.jsx
@@ -45,7 +45,7 @@ const MessageHeader = ({ isSearchView = false }) => {
<>
(endpoint === 'chatGPTBrowser' ? null : setSaveAsDialogShow(true))}
diff --git a/client/src/components/Nav/ClearConvos.jsx b/client/src/components/Nav/ClearConvos.jsx
index 7eac1e1ff2..770eaacd98 100644
--- a/client/src/components/Nav/ClearConvos.jsx
+++ b/client/src/components/Nav/ClearConvos.jsx
@@ -1,11 +1,10 @@
import { useEffect } from 'react';
import store from '~/store';
-import TrashIcon from '../svg/TrashIcon';
-import { Dialog, DialogTrigger } from '../ui/Dialog.tsx';
+import { Dialog } from '../ui/Dialog.tsx';
import DialogTemplate from '../ui/DialogTemplate';
import { useClearConversationsMutation } from '~/data-provider';
-export default function ClearConvos() {
+const ClearConvos = ({ open, onOpenChange}) => {
const { newConversation } = store.useConversation();
const { refreshConversations } = store.useConversations();
const clearConvosMutation = useClearConversationsMutation();
@@ -23,13 +22,7 @@ export default function ClearConvos() {
}, [clearConvosMutation.isSuccess]);
return (
-
-
-
-
- Clear conversations
-
-
+
);
-}
+};
+
+export default ClearConvos;
diff --git a/client/src/components/Nav/DarkMode.jsx b/client/src/components/Nav/DarkMode.jsx
index 753f56566d..53577f659c 100644
--- a/client/src/components/Nav/DarkMode.jsx
+++ b/client/src/components/Nav/DarkMode.jsx
@@ -1,9 +1,9 @@
-import React, { useState, useContext } from 'react';
+import { forwardRef, useContext } from 'react';
import DarkModeIcon from '../svg/DarkModeIcon';
import LightModeIcon from '../svg/LightModeIcon';
import { ThemeContext } from '~/hooks/ThemeContext';
-export default function DarkMode() {
+const DarkMode = forwardRef(() => {
const { theme, setTheme } = useContext(ThemeContext);
const clickHandler = () => setTheme(theme === 'dark' ? 'light' : 'dark');
@@ -18,4 +18,6 @@ export default function DarkMode() {
{mode}
);
-}
+});
+
+export default DarkMode;
diff --git a/client/src/components/Nav/ExportConversation/ExportModel.jsx b/client/src/components/Nav/ExportConversation/ExportModel.jsx
index a91daeecdb..1366ef29ae 100644
--- a/client/src/components/Nav/ExportConversation/ExportModel.jsx
+++ b/client/src/components/Nav/ExportConversation/ExportModel.jsx
@@ -1,10 +1,10 @@
-import React, { useEffect, useState } from 'react';
+import { useEffect, useState } from 'react';
import { useRecoilValue, useRecoilCallback } from 'recoil';
import filenamify from 'filenamify';
import exportFromJSON from 'export-from-json';
import download from 'downloadjs';
import DialogTemplate from '~/components/ui/DialogTemplate.jsx';
-import { Dialog, DialogClose, DialogButton } from '~/components/ui/Dialog.tsx';
+import { Dialog, DialogButton } from '~/components/ui/Dialog.tsx';
import { Input } from '~/components/ui/Input.tsx';
import { Label } from '~/components/ui/Label.tsx';
import { Checkbox } from '~/components/ui/Checkbox.tsx';
diff --git a/client/src/components/Nav/ExportConversation/index.jsx b/client/src/components/Nav/ExportConversation/index.jsx
index d5742a508d..e883dce417 100644
--- a/client/src/components/Nav/ExportConversation/index.jsx
+++ b/client/src/components/Nav/ExportConversation/index.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import { useState, forwardRef } from 'react';
import { useRecoilValue } from 'recoil';
import { Download } from 'lucide-react';
import { cn } from '~/utils/';
@@ -7,7 +7,7 @@ import ExportModel from './ExportModel';
import store from '~/store';
-export default function ExportConversation() {
+const ExportConversation = forwardRef(() => {
const [open, setOpen] = useState(false);
const conversation = useRecoilValue(store.conversation) || {};
@@ -37,4 +37,6 @@ export default function ExportConversation() {
>
);
-}
+});
+
+export default ExportConversation;
\ No newline at end of file
diff --git a/client/src/components/Nav/Logout.jsx b/client/src/components/Nav/Logout.jsx
index 035008deac..212a30bc36 100644
--- a/client/src/components/Nav/Logout.jsx
+++ b/client/src/components/Nav/Logout.jsx
@@ -1,8 +1,8 @@
-import React from 'react';
+import { forwardRef } from 'react';
import LogOutIcon from '../svg/LogOutIcon';
import { useAuthContext } from '~/hooks/AuthContext';
-export default function Logout() {
+const Logout = forwardRef(() => {
const { user, logout } = useAuthContext();
const handleLogout = () => {
@@ -20,4 +20,6 @@ export default function Logout() {
Log out
);
-}
+});
+
+export default Logout;
diff --git a/client/src/components/Nav/NavLink.jsx b/client/src/components/Nav/NavLink.jsx
index bd677406a9..85303d46ef 100644
--- a/client/src/components/Nav/NavLink.jsx
+++ b/client/src/components/Nav/NavLink.jsx
@@ -1,20 +1,25 @@
-import React from 'react';
+import { forwardRef } from 'react';
+import { cn } from '~/utils/';
-export default function NavLink({ svg, text, clickHandler }) {
- const props = {
- className:
- 'flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10'
- };
+const NavLink = forwardRef((props, ref) => {
+ const { svg, text, clickHandler, className = '' } = props;
+ const defaultProps = {};
+
+ defaultProps.className = cn(
+ 'flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10',
+ className
+ );
if (clickHandler) {
- props.onClick = clickHandler;
- console.log('clickHandler: ', clickHandler);
+ defaultProps.onClick = clickHandler;
}
return (
-
+
{svg()}
{text}
);
-}
+});
+
+export default NavLink;
diff --git a/client/src/components/Nav/NavLinks.jsx b/client/src/components/Nav/NavLinks.jsx
index 96c46259df..b1bcf50848 100644
--- a/client/src/components/Nav/NavLinks.jsx
+++ b/client/src/components/Nav/NavLinks.jsx
@@ -1,70 +1,111 @@
import { Menu, Transition } from '@headlessui/react';
-import { Fragment, useEffect, useRef, useState } from 'react';
+import { Fragment, useState } from 'react';
+import { useRecoilValue } from 'recoil';
import SearchBar from './SearchBar';
+import TrashIcon from '../svg/TrashIcon';
+import { Download } from 'lucide-react';
+import NavLink from './NavLink';
+import ExportModel from './ExportConversation/ExportModel';
import ClearConvos from './ClearConvos';
import DarkMode from './DarkMode';
import Logout from './Logout';
-import ExportConversation from './ExportConversation';
import { useAuthContext } from '~/hooks/AuthContext';
import { cn } from '~/utils/';
import DotsIcon from '../svg/DotsIcon';
+import store from '~/store';
+
export default function NavLinks({ clearSearch, isSearchEnabled }) {
- const { user, logout } = useAuthContext();
+ const [showExports, setShowExports] = useState(false);
+ const [showClearConvos, setShowClearConvos] = useState(false);
+ const { user } = useAuthContext();
+
+ const conversation = useRecoilValue(store.conversation) || {};
+
+ const exportable =
+ conversation?.conversationId &&
+ conversation?.conversationId !== 'new' &&
+ conversation?.conversationId !== 'search';
+
+ const clickHandler = () => {
+ if (exportable) setShowExports(true);
+ };
+
return (
-
- {({ open }) => (
- <>
-
-
-
-
+ <>
+
+ {({ open }) => (
+ <>
+
+
+
+
+
-
-
- {user?.name || 'USER'}
-
-
-
+
+ {user?.name || 'USER'}
+
+
+
-
-
-
- {({}) => <>{!!isSearchEnabled && }>}
-
- {({}) => }
-
-
- {({}) => }
- {({}) => }
-
-
-
-
-
-
-
- >
- )}
-
+
+
+
+ {!!isSearchEnabled && }
+
+
+ }
+ text="Export conversation"
+ clickHandler={clickHandler}
+ />
+
+
+
+
+
+
+ }
+ text="Clear conversations"
+ clickHandler={() => setShowClearConvos(true)}
+ />
+
+
+
+
+
+
+
+ >
+ )}
+
+ {showExports &&
}
+ {showClearConvos &&
}
+ >
);
}
diff --git a/client/src/components/Nav/SearchBar.jsx b/client/src/components/Nav/SearchBar.jsx
index afe9032d55..e982b92c8a 100644
--- a/client/src/components/Nav/SearchBar.jsx
+++ b/client/src/components/Nav/SearchBar.jsx
@@ -1,8 +1,10 @@
+import { forwardRef } from 'react';
import { Search } from 'lucide-react';
import { useRecoilState } from 'recoil';
import store from '~/store';
-export default function SearchBar({ clearSearch }) {
+const SearchBar = forwardRef((props, ref) => {
+ const { clearSearch } = props;
const [searchQuery, setSearchQuery] = useRecoilState(store.searchQuery);
const handleKeyUp = (e) => {
@@ -19,7 +21,10 @@ export default function SearchBar({ clearSearch }) {
};
return (
-
+
{ }
);
-}
+});
+
+export default SearchBar;
diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx
index b3d1907f95..ac3ff9dc7a 100644
--- a/client/src/components/Nav/index.jsx
+++ b/client/src/components/Nav/index.jsx
@@ -1,5 +1,6 @@
import { useState, useEffect, useRef, useContext } from 'react';
import NewChat from './NewChat';
+import Panel from '../svg/Panel';
import Spinner from '../svg/Spinner';
import Pages from '../Conversations/Pages';
import Conversations from '../Conversations';
@@ -198,62 +199,21 @@ export default function Nav({ navVisible, setNavVisible }) {
Close sidebar
-
-
-
-
-
-
-
+
{!navVisible && (
Open sidebar
-
-
-
+
)}
diff --git a/client/src/components/svg/Panel.jsx b/client/src/components/svg/Panel.jsx
new file mode 100644
index 0000000000..c82a2ec461
--- /dev/null
+++ b/client/src/components/svg/Panel.jsx
@@ -0,0 +1,47 @@
+import { cn } from '~/utils/';
+
+export default function Panel({ open = false, className }) {
+ const openPanel = (
+
+
+
+
+
+ );
+
+ const closePanel = (
+
+
+
+
+
+ );
+
+ if (open) {
+ return openPanel;
+ } else {
+ return closePanel;
+ }
+}
diff --git a/client/src/components/ui/DialogTemplate.jsx b/client/src/components/ui/DialogTemplate.jsx
index 5d4d1eb249..0dc932ef44 100644
--- a/client/src/components/ui/DialogTemplate.jsx
+++ b/client/src/components/ui/DialogTemplate.jsx
@@ -1,5 +1,4 @@
-import React from 'react';
-
+import { forwardRef } from 'react';
import {
DialogClose,
DialogContent,
@@ -10,21 +9,22 @@ import {
} from './Dialog.tsx';
import { cn } from '~/utils/';
-export default function DialogTemplate({
- title,
- description,
- main,
- buttons,
- leftButtons,
- selection,
- className
-}) {
+const DialogTemplate = forwardRef((props, ref) => {
+ const {
+ title,
+ description,
+ main,
+ buttons,
+ leftButtons,
+ selection,
+ className
+ } = props;
const { selectHandler, selectClasses, selectText } = selection || {};
const defaultSelect =
'bg-gray-900 text-white transition-colors hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200 dark:focus:ring-gray-400 dark:focus:ring-offset-gray-900';
return (
-
+
{title}
@@ -51,4 +51,6 @@ export default function DialogTemplate({
);
-}
+});
+
+export default DialogTemplate;
diff --git a/client/src/utils/getIcon.jsx b/client/src/utils/getIcon.jsx
index d84fdc07f8..c1c9c4ff49 100644
--- a/client/src/utils/getIcon.jsx
+++ b/client/src/utils/getIcon.jsx
@@ -1,6 +1,3 @@
-import { clsx } from 'clsx';
-import React from 'react';
-import { twMerge } from 'tailwind-merge';
import GPTIcon from '../components/svg/GPTIcon';
import BingIcon from '../components/svg/BingIcon';
import { useAuthContext } from '~/hooks/AuthContext';
@@ -8,12 +5,13 @@ import { useAuthContext } from '~/hooks/AuthContext';
const getIcon = (props) => {
// { size = 30, isCreatedByUser, model, chatGptLabel, error, ...props }
const { size = 30, isCreatedByUser, button, model } = props;
- const { user, logout } = useAuthContext();
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const { user } = useAuthContext();
if (isCreatedByUser)
return (
{
className="rounded-sm"
src={
user?.avatar ||
- `https://api.dicebear.com/6.x/initials/svg?seed=${user?.name}&fontFamily=Verdana&fontSize=36`
+ `https://api.dicebear.com/6.x/initials/svg?seed=${user?.name || 'User'}&fontFamily=Verdana&fontSize=36`
}
alt="avatar"
/>
diff --git a/package-lock.json b/package-lock.json
index 82a4692301..0f27d4160c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -114,7 +114,7 @@
"filenamify": "^6.0.0",
"html2canvas": "^1.4.1",
"lodash": "^4.17.21",
- "lucide-react": "^0.113.0",
+ "lucide-react": "^0.220.0",
"pino": "^8.12.1",
"rc-input-number": "^7.4.2",
"react": "^18.2.0",
@@ -12104,8 +12104,9 @@
}
},
"node_modules/lucide-react": {
- "version": "0.113.0",
- "license": "ISC",
+ "version": "0.220.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.220.0.tgz",
+ "integrity": "sha512-bYtGUsLAWBvZu+BzAU/ziP1gzE4LwMEXLnlgSr1yUKEPPalLG77JLd5GdYebOVkpm+GtqRqnp6tEKDX7Bm8ZlQ==",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
}
@@ -22692,7 +22693,7 @@
"filenamify": "^6.0.0",
"html2canvas": "^1.4.1",
"lodash": "^4.17.21",
- "lucide-react": "^0.113.0",
+ "lucide-react": "0.220.0",
"path": "^0.12.7",
"pino": "^8.12.1",
"postcss": "^8.4.21",
@@ -26275,7 +26276,9 @@
}
},
"lucide-react": {
- "version": "0.113.0",
+ "version": "0.220.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.220.0.tgz",
+ "integrity": "sha512-bYtGUsLAWBvZu+BzAU/ziP1gzE4LwMEXLnlgSr1yUKEPPalLG77JLd5GdYebOVkpm+GtqRqnp6tEKDX7Bm8ZlQ==",
"requires": {}
},
"magic-string": {