diff --git a/client/public/assets/favicon-16x16.png b/client/public/assets/favicon-16x16.png index 16f72e5ff1..03975d8ec0 100644 Binary files a/client/public/assets/favicon-16x16.png and b/client/public/assets/favicon-16x16.png differ diff --git a/client/public/assets/favicon-32x32.png b/client/public/assets/favicon-32x32.png index ed67942c78..df89fb33b0 100644 Binary files a/client/public/assets/favicon-32x32.png and b/client/public/assets/favicon-32x32.png differ diff --git a/client/src/components/Conversations/DeleteButton.jsx b/client/src/components/Conversations/DeleteButton.tsx similarity index 95% rename from client/src/components/Conversations/DeleteButton.jsx rename to client/src/components/Conversations/DeleteButton.tsx index fec6a6d246..4d0545cd93 100644 --- a/client/src/components/Conversations/DeleteButton.jsx +++ b/client/src/components/Conversations/DeleteButton.tsx @@ -22,7 +22,7 @@ export default function DeleteButton({ conversationId, renaming, retainView, tit useEffect(() => { if (deleteConvoMutation.isSuccess) { - if (currentConversation?.conversationId == conversationId) { + if ((currentConversation as { conversationId?: string }).conversationId == conversationId) { newConversation(); } diff --git a/client/src/components/Conversations/RenameButton.jsx b/client/src/components/Conversations/RenameButton.jsx deleted file mode 100644 index b3e5be470a..0000000000 --- a/client/src/components/Conversations/RenameButton.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import RenameIcon from '../svg/RenameIcon'; -import CheckMark from '../svg/CheckMark'; - -export default function RenameButton({ renaming, renameHandler, onRename, twcss }) { - const handler = renaming ? onRename : renameHandler; - const classProp = { className: 'p-1 hover:text-white' }; - if (twcss) { - classProp.className = twcss; - } - return ( - - ); -} diff --git a/client/src/components/Conversations/RenameButton.tsx b/client/src/components/Conversations/RenameButton.tsx new file mode 100644 index 0000000000..cd6dcba306 --- /dev/null +++ b/client/src/components/Conversations/RenameButton.tsx @@ -0,0 +1,28 @@ +import React, { ReactElement } from 'react'; +import RenameIcon from '../svg/RenameIcon'; +import CheckMark from '../svg/CheckMark'; + +interface RenameButtonProps { + renaming: boolean; + renameHandler: () => void; + onRename: () => void; + twcss?: string; +} + +export default function RenameButton({ + renaming, + renameHandler, + onRename, + twcss, +}: RenameButtonProps): ReactElement { + const handler = renaming ? onRename : renameHandler; + const classProp: { className?: string } = { className: 'p-1 hover:text-white' }; + if (twcss) { + classProp.className = twcss; + } + return ( + + ); +} diff --git a/client/src/components/Endpoints/Icon.tsx b/client/src/components/Endpoints/Icon.tsx index bba80ae50e..9966d1988f 100644 --- a/client/src/components/Endpoints/Icon.tsx +++ b/client/src/components/Endpoints/Icon.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Plugin, GPTIcon, AnthropicIcon } from '~/components/svg'; +import { Plugin, GPTIcon, AnthropicIcon, AzureMinimalIcon } from '~/components/svg'; import { useAuthContext } from '~/hooks'; import { cn } from '~/utils'; import { IconProps } from '~/common'; @@ -34,12 +34,12 @@ const Icon: React.FC = (props) => { } else { const endpointIcons = { azureOpenAI: { - icon: , + icon: , bg: 'linear-gradient(0.375turn, #61bde2, #4389d0)', name: 'ChatGPT', }, openAI: { - icon: , + icon: , bg: typeof model === 'string' && model.toLowerCase().includes('gpt-4') ? '#AB68FF' @@ -52,7 +52,7 @@ const Icon: React.FC = (props) => { name: 'Plugins', }, google: { icon: Palm Icon, name: 'PaLM2' }, - anthropic: { icon: , bg: '#d09a74', name: 'Claude' }, + anthropic: { icon: , bg: '#d09a74', name: 'Claude' }, bingAI: { icon: jailbreak ? ( Bing Icon @@ -62,7 +62,7 @@ const Icon: React.FC = (props) => { name: jailbreak ? 'Sydney' : 'BingAI', }, chatGPTBrowser: { - icon: , + icon: , bg: typeof model === 'string' && model.toLowerCase().includes('gpt-4') ? '#AB68FF' @@ -73,7 +73,7 @@ const Icon: React.FC = (props) => { default: { icon: , bg: 'grey', name: 'UNKNOWN' }, }; - const { icon, bg, name } = endpointIcons[endpoint ?? ''] ?? endpointIcons.default; + const { icon, bg, name } = endpointIcons[endpoint ?? 'default']; return (
void; + selectedEndpoint: string; +} + +export default function EndpointItems({ endpoints, selectedEndpoint }: EndpointItemsProps) { return ( <> {endpoints.map((endpoint) => ( @@ -8,7 +14,6 @@ export default function EndpointItems({ endpoints, onSelect, selectedEndpoint }) isSelected={selectedEndpoint === endpoint} key={endpoint} value={endpoint} - onSelect={onSelect} endpoint={endpoint} /> ))} diff --git a/client/src/components/Input/EndpointMenu/PresetItem.tsx b/client/src/components/Input/EndpointMenu/PresetItem.tsx index a3f7686379..297363d786 100644 --- a/client/src/components/Input/EndpointMenu/PresetItem.tsx +++ b/client/src/components/Input/EndpointMenu/PresetItem.tsx @@ -17,6 +17,7 @@ export default function PresetItem({ model: preset?.model, error: false, className: 'mr-2', + isCreatedByUser: false, }); const getPresetTitle = () => { diff --git a/client/src/components/Input/Generations/__tests__/Button.spec.tsx b/client/src/components/Input/Generations/__tests__/Button.spec.tsx new file mode 100644 index 0000000000..77c71a1353 --- /dev/null +++ b/client/src/components/Input/Generations/__tests__/Button.spec.tsx @@ -0,0 +1,30 @@ +import { render, fireEvent } from '@testing-library/react'; +import Button from '../Button'; + +describe('Button', () => { + it('renders with the correct type and children', () => { + const { getByTestId, getByText } = render( + , + ); + expect(getByTestId('regenerate-generation-button')).toBeInTheDocument(); + expect(getByText('Regenerate')).toBeInTheDocument(); + }); + + it('calls onClick when clicked', () => { + const handleClick = jest.fn(); + const { getByText } = render( + , + ); + fireEvent.click(getByText('Continue')); + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/client/src/components/Input/Generations/__tests__/Continue.spec.tsx b/client/src/components/Input/Generations/__tests__/Continue.spec.tsx new file mode 100644 index 0000000000..e9ac7beebb --- /dev/null +++ b/client/src/components/Input/Generations/__tests__/Continue.spec.tsx @@ -0,0 +1,22 @@ +import { render, fireEvent } from '@testing-library/react'; +import Continue from '../Continue'; + +describe('Continue', () => { + it('should render the Continue button', () => { + const { getByText } = render( + { + (''); + }} + />, + ); + expect(getByText('Continue')).toBeInTheDocument(); + }); + + it('should call onClick when the button is clicked', () => { + const handleClick = jest.fn(); + const { getByText } = render(); + fireEvent.click(getByText('Continue')); + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/client/src/components/Input/Generations/__tests__/Regenerate.spec.tsx b/client/src/components/Input/Generations/__tests__/Regenerate.spec.tsx new file mode 100644 index 0000000000..7cf4f5b1d5 --- /dev/null +++ b/client/src/components/Input/Generations/__tests__/Regenerate.spec.tsx @@ -0,0 +1,22 @@ +import { render, fireEvent } from '@testing-library/react'; +import Regenerate from '../Regenerate'; + +describe('Regenerate', () => { + it('should render the Regenerate button', () => { + const { getByText } = render( + { + (''); + }} + />, + ); + expect(getByText('Regenerate')).toBeInTheDocument(); + }); + + it('should call onClick when the button is clicked', () => { + const handleClick = jest.fn(); + const { getByText } = render(); + fireEvent.click(getByText('Regenerate')); + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/client/src/components/Input/Generations/__tests__/Stop.spec.tsx b/client/src/components/Input/Generations/__tests__/Stop.spec.tsx new file mode 100644 index 0000000000..fd85c60b79 --- /dev/null +++ b/client/src/components/Input/Generations/__tests__/Stop.spec.tsx @@ -0,0 +1,22 @@ +import { render, fireEvent } from '@testing-library/react'; +import Stop from '../Stop'; + +describe('Stop', () => { + it('should render the Stop button', () => { + const { getByText } = render( + { + (''); + }} + />, + ); + expect(getByText('Stop')).toBeInTheDocument(); + }); + + it('should call onClick when the button is clicked', () => { + const handleClick = jest.fn(); + const { getByText } = render(); + fireEvent.click(getByText('Stop')); + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/client/src/components/Messages/Message.tsx b/client/src/components/Messages/Message.tsx index 1938128f25..876cdb34f6 100644 --- a/client/src/components/Messages/Message.tsx +++ b/client/src/components/Messages/Message.tsx @@ -94,6 +94,7 @@ export default function Message({ ...conversation, ...message, model: message?.model ?? conversation?.model, + size: 38, }); if (message?.bg && searchResult) { @@ -135,7 +136,7 @@ export default function Message({ <>
-
+
{typeof icon === 'string' && /[^\\x00-\\x7F]+/.test(icon as string) ? ( {icon} ) : ( diff --git a/client/src/components/Nav/ClearConvos.tsx b/client/src/components/Nav/ClearConvos.tsx index 0d76d8bb98..bdefb4c53d 100644 --- a/client/src/components/Nav/ClearConvos.tsx +++ b/client/src/components/Nav/ClearConvos.tsx @@ -12,6 +12,7 @@ const ClearConvos = ({ open, onOpenChange }) => { const [confirmClear, setConfirmClear] = useState(false); const localize = useLocalize(); + // Clear all conversations const clearConvos = useCallback(() => { if (confirmClear) { console.log('Clearing conversations...'); @@ -22,6 +23,7 @@ const ClearConvos = ({ open, onOpenChange }) => { } }, [confirmClear, clearConvosMutation]); + // Refresh conversations after clearing useEffect(() => { if (clearConvosMutation.isSuccess) { refreshConversations(); diff --git a/client/src/components/Nav/MobileNav.jsx b/client/src/components/Nav/MobileNav.tsx similarity index 100% rename from client/src/components/Nav/MobileNav.jsx rename to client/src/components/Nav/MobileNav.tsx diff --git a/client/src/components/Nav/NavLink.jsx b/client/src/components/Nav/NavLink.jsx deleted file mode 100644 index 38b6f6a2d6..0000000000 --- a/client/src/components/Nav/NavLink.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { forwardRef } from 'react'; -import { cn } from '~/utils/'; - -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) { - defaultProps.onClick = clickHandler; - } - - return ( - - {svg()} - {text} - - ); -}); - -export default NavLink; diff --git a/client/src/components/Nav/NavLink.tsx b/client/src/components/Nav/NavLink.tsx new file mode 100644 index 0000000000..519e274226 --- /dev/null +++ b/client/src/components/Nav/NavLink.tsx @@ -0,0 +1,35 @@ +import { FC, forwardRef } from 'react'; +import { cn } from '~/utils/'; + +interface Props { + svg: () => JSX.Element; + text: string; + clickHandler?: () => void; + className?: string; +} + +const NavLink: FC = forwardRef((props, ref) => { + const { svg, text, clickHandler, className = '' } = props; + const defaultProps: { + className: string; + onClick?: () => void; + } = { + 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) { + defaultProps.onClick = clickHandler; + } + + return ( + + {svg()} + {text} + + ); +}); + +export default NavLink; diff --git a/client/src/components/Nav/NavLinks.jsx b/client/src/components/Nav/NavLinks.tsx similarity index 100% rename from client/src/components/Nav/NavLinks.jsx rename to client/src/components/Nav/NavLinks.tsx diff --git a/client/src/components/Nav/NewChat.jsx b/client/src/components/Nav/NewChat.tsx similarity index 100% rename from client/src/components/Nav/NewChat.jsx rename to client/src/components/Nav/NewChat.tsx diff --git a/client/src/components/svg/AzureMinimalIcon.tsx b/client/src/components/svg/AzureMinimalIcon.tsx index 21797d6871..50c3af8ff2 100644 --- a/client/src/components/svg/AzureMinimalIcon.tsx +++ b/client/src/components/svg/AzureMinimalIcon.tsx @@ -1,7 +1,10 @@ /* eslint-disable indent */ -import React from 'react'; +import { cn } from '~/utils/'; + +export default function AzureMinimalIcon({ size = 25, className = 'h-4 w-4' }) { + const height = size; + const width = size; -export default function AzureMinimalIcon() { return (