Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

Commit

Permalink
Merge branch 'pr-299-chat-branches-called-versioned-refreshes-by-pr-a…
Browse files Browse the repository at this point in the history
…uthor'
  • Loading branch information
zewebdev1337 committed May 28, 2024
2 parents 7c7a555 + 43dd181 commit 443c5e2
Show file tree
Hide file tree
Showing 25 changed files with 463 additions and 188 deletions.
48 changes: 9 additions & 39 deletions src/components/ApiMenu/ApiMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import React, { useEffect, useState } from 'react';
import { useTranslation, Trans } from 'react-i18next';
import useStore from '@store/store';

import useHideOnOutsideClick from '@hooks/useHideOnOutsideClick';

import PopupModal from '@components/PopupModal';

import { availableEndpoints, defaultAPIEndpoint } from '@constants/auth';

import DownChevronArrow from '@icon/DownChevronArrow';
import DropDown from '@components/DropDown';

const ApiMenu = ({
setIsModalOpen,
Expand Down Expand Up @@ -144,44 +142,16 @@ const ApiEndpointSelector = ({
_apiEndpoint: string;
_setApiEndpoint: React.Dispatch<React.SetStateAction<string>>;
}) => {
const [dropDown, setDropDown, dropDownRef] = useHideOnOutsideClick();

return (
<div className='w-[40vw] relative flex-1'>
<button
className='btn btn-neutral btn-small flex justify-between w-full'
type='button'
aria-label='expand api menu'
onClick={() => setDropDown((prev) => !prev)}
>
<span className='truncate'>{_apiEndpoint}</span>
<DownChevronArrow />
</button>
<div
id='dropdown'
ref={dropDownRef}
className={`${
dropDown ? '' : 'hidden'
} absolute top-100 bottom-100 z-10 bg-white rounded-lg shadow-xl border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group dark:bg-gray-800 opacity-90 w-32 w-full`}
>
<ul
className='text-sm text-gray-700 dark:text-gray-200 p-0 m-0'
aria-labelledby='dropdownDefaultButton'
>
{availableEndpoints.map((endpoint) => (
<li
className='px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white cursor-pointer truncate'
onClick={() => {
_setApiEndpoint(endpoint);
setDropDown(false);
}}
key={endpoint}
>
{endpoint}
</li>
))}
</ul>
</div>
<DropDown
selected={_apiEndpoint}
selections={availableEndpoints.map((e) => ({ value: e, label: e }))}
dropDownStyles={['w-32', 'w-full']}
onClick={(value) => {
_setApiEndpoint(value);
}}
/>
</div>
);
};
Expand Down
26 changes: 15 additions & 11 deletions src/components/Chat/ChatContent/ChatContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,27 @@ const ChatContent = () => {
<NewMessageButton messageIndex={-1} />
)}
{messages?.map((message, index) => (
(advancedMode || index !== 0 || message.role !== 'system') && (
<React.Fragment key={index}>
<Message
role={message.role}
content={message.content}
messageIndex={index}
/>
{!generating && advancedMode && <NewMessageButton messageIndex={index} />}
</React.Fragment>
)
<React.Fragment key={index}>
<Message
role={message.role}
content={message.content}
messageIndex={index}
generating={generating}
versions={message.versions}
versionIdx={message.versionIndex}
/>
{!generating && advancedMode && (
<NewMessageButton messageIndex={index} />
)}
</React.Fragment>
))}
</div>

<Message
role={inputRole}
content=''
messageIndex={stickyIndex}
generating={generating}
sticky
/>
{error !== '' && (
Expand All @@ -105,7 +109,7 @@ const ChatContent = () => {
: 'md:max-w-3xl lg:max-w-3xl xl:max-w-4xl'
}`}
>
{useStore.getState().generating || (
{generating || (
<div className='md:w-[calc(100%-50px)] flex gap-4 flex-wrap justify-center'>
<DownloadChat saveRef={saveRef} />
<ShareGPT />
Expand Down
29 changes: 29 additions & 0 deletions src/components/Chat/ChatContent/Message/Button/BaseButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

const BaseButton = ({
onClick,
icon,
disabled = false,
}: {
onClick: React.MouseEventHandler<HTMLButtonElement>;
icon: React.ReactElement;
disabled?: boolean;
}) => {
return (
<div className='text-gray-400 flex self-end lg:self-center justify-center gap-3 md:gap-4 visible'>
<button
className={`p-1 rounded-md dark:text-gray-400 md:invisible md:group-hover:visible ${
disabled
? 'disabled:dark:hover:text-gray-400 cursor-default'
: 'hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-700 dark:hover:text-gray-200'
}`}
onClick={disabled ? () => {} : onClick}
disabled={disabled}
>
{icon}
</button>
</div>
);
};

export default BaseButton;
23 changes: 23 additions & 0 deletions src/components/Chat/ChatContent/Message/Button/LeftButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

import DownChevronArrow from '@icon/DownChevronArrow';

import BaseButton from './BaseButton';

const LeftButton = ({
disabled,
onClick,
}: {
disabled: boolean;
onClick: React.MouseEventHandler<HTMLButtonElement>;
}) => {
return (
<BaseButton
icon={<DownChevronArrow className='rotate-90 m-1' />}
disabled={disabled}
onClick={onClick}
/>
);
};

export default LeftButton;
27 changes: 27 additions & 0 deletions src/components/Chat/ChatContent/Message/Button/RightButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';

import DownChevronArrow from '@icon/DownChevronArrow';

import BaseButton from './BaseButton';

const RightButton = ({
disabled,
onClick,
}: {
disabled: boolean;
onClick: React.MouseEventHandler<HTMLButtonElement>;
}) => {
return (
<BaseButton
icon={
<DownChevronArrow
className='rotate-[270deg] m-1'
/>
}
disabled={disabled}
onClick={onClick}
/>
);
};

export default RightButton;
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const CommandPrompt = ({
}, [prompts]);

return (
<div className='relative max-wd-sm' ref={dropDownRef}>
<div className='relative max-wd-sm' ref={dropDownRef as React.RefObject<HTMLDivElement>}>
<button
className='btn btn-neutral btn-small'
aria-label='prompt library'
Expand Down
119 changes: 111 additions & 8 deletions src/components/Chat/ChatContent/Message/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

import DropDown from '@components/DropDown';

import useStore from '@store/store';

import { ChatInterface, Role, roles } from '@type/chat';

import Avatar from './Avatar';
import MessageContent from './MessageContent';

import { Role } from '@type/chat';
import RoleSelector from './RoleSelector';
import LeftButton from './Button/LeftButton';
import RightButton from './Button/RightButton';

// const backgroundStyle: { [role in Role]: string } = {
// user: 'dark:bg-gray-800',
Expand All @@ -19,15 +25,67 @@ const Message = React.memo(
role,
content,
messageIndex,
generating,
versions,
versionIdx,
sticky = false,
}: {
role: Role;
content: string;
messageIndex: number;
generating: boolean;
versions?: string[] | undefined;
versionIdx?: number | undefined;
sticky?: boolean;
}) => {
const { t } = useTranslation();

const setChats = useStore((state) => state.setChats);
const setInputRole = useStore((state) => state.setInputRole);

const hideSideMenu = useStore((state) => state.hideSideMenu);
const advancedMode = useStore((state) => state.advancedMode);
const currentChatIndex = useStore((state) => state.currentChatIndex);

const previousVersion = () => {
if (versions && versions.length !== undefined) {
const idx = versionIdx !== undefined ? versionIdx : versions.length - 1;
return idx - 1;
}
return undefined;
};

const hasPreviousVersion = () => {
const previous = previousVersion();
return previous !== undefined && previous >= 0;
};

const nextVersion = () => {
if (versions && versions.length !== undefined) {
const idx = versionIdx !== undefined ? versionIdx : versions.length - 1;
return idx + 1;
}
return undefined;
};

const hasNextVersion = () => {
const next = nextVersion();
return next !== undefined && versions && next < versions.length;
};

const setVersion = (index: number, content: string) => {
const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(useStore.getState().chats)
);

const updatedMessages = updatedChats[currentChatIndex].messages;
let version = updatedMessages[messageIndex];

version.content = content;
version.versionIndex = index;

setChats(updatedChats);
};

return (
<div
Expand All @@ -44,12 +102,57 @@ const Message = React.memo(
>
<Avatar role={role} />
<div className='w-[calc(100%-50px)] '>
{advancedMode &&
<RoleSelector
role={role}
messageIndex={messageIndex}
sticky={sticky}
/>}
<div className='relative flex flex-row justify-between items-center; gap-1 md:gap-3 lg:w-[calc(100%-115px)]'>
{advancedMode && (
<DropDown
selected={t(role)}
selections={roles.map((r) => ({ value: r, label: t(r) }))}
onClick={(value) => {
if (!sticky) {
const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(useStore.getState().chats)
);
updatedChats[currentChatIndex].messages[
messageIndex
].role = value as Role;
setChats(updatedChats);
} else {
setInputRole(value as Role);
}
}}
/>
)}

{!sticky && versions?.length && versions.length > 1 && (
<div className='flex items-center justify-center'>
<LeftButton
disabled={generating || !hasPreviousVersion()}
onClick={() => {
const previous = previousVersion();

if (previous !== undefined && previous >= 0) {
setVersion(previous, versions[previous]);
}
}}
/>

<span className='text-xs pl-2 pr-2'>{`${
(versionIdx || 0) + 1
} / ${versions.length}`}</span>

<RightButton
disabled={generating || !hasNextVersion()}
onClick={() => {
const next = nextVersion();

if (next !== undefined && next <= versions.length - 1) {
setVersion(next, versions[next]);
}
}}
/>
</div>
)}
</div>
<MessageContent
role={role}
content={content}
Expand Down
Loading

0 comments on commit 443c5e2

Please sign in to comment.