mirror of
https://github.com/NovaOSS/nova-betterchat.git
synced 2024-11-26 00:13:59 +01:00
parent
59466e987a
commit
4fa5c14734
12
src/App.tsx
12
src/App.tsx
|
@ -6,38 +6,30 @@ import Menu from './components/Menu';
|
||||||
import ConfigMenu from './components/ConfigMenu';
|
import ConfigMenu from './components/ConfigMenu';
|
||||||
|
|
||||||
import useSaveToLocalStorage from '@hooks/useSaveToLocalStorage';
|
import useSaveToLocalStorage from '@hooks/useSaveToLocalStorage';
|
||||||
import useUpdateCharts from '@hooks/useUpdateChats';
|
|
||||||
import useInitialiseNewChat from '@hooks/useInitialiseNewChat';
|
import useInitialiseNewChat from '@hooks/useInitialiseNewChat';
|
||||||
|
|
||||||
import { ChatInterface } from '@type/chat';
|
import { ChatInterface } from '@type/chat';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
useSaveToLocalStorage();
|
useSaveToLocalStorage();
|
||||||
useUpdateCharts();
|
|
||||||
const initialiseNewChat = useInitialiseNewChat();
|
const initialiseNewChat = useInitialiseNewChat();
|
||||||
|
|
||||||
const [setChats, setMessages, setCurrentChatIndex] = useStore((state) => [
|
const setChats = useStore((state) => state.setChats);
|
||||||
state.setChats,
|
const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
|
||||||
state.setMessages,
|
|
||||||
state.setCurrentChatIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// localStorage.removeItem('chats');
|
|
||||||
const storedChats = localStorage.getItem('chats');
|
const storedChats = localStorage.getItem('chats');
|
||||||
if (storedChats) {
|
if (storedChats) {
|
||||||
try {
|
try {
|
||||||
const chats: ChatInterface[] = JSON.parse(storedChats);
|
const chats: ChatInterface[] = JSON.parse(storedChats);
|
||||||
if (chats.length > 0) {
|
if (chats.length > 0) {
|
||||||
setChats(chats);
|
setChats(chats);
|
||||||
setMessages(chats[0].messages);
|
|
||||||
setCurrentChatIndex(0);
|
setCurrentChatIndex(0);
|
||||||
} else {
|
} else {
|
||||||
initialiseNewChat();
|
initialiseNewChat();
|
||||||
}
|
}
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
setChats([]);
|
setChats([]);
|
||||||
setMessages([]);
|
|
||||||
setCurrentChatIndex(-1);
|
setCurrentChatIndex(-1);
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { createRef, useState } from 'react';
|
import React from 'react';
|
||||||
import ScrollToBottom from 'react-scroll-to-bottom';
|
import ScrollToBottom from 'react-scroll-to-bottom';
|
||||||
import useStore from '@store/store';
|
import useStore from '@store/store';
|
||||||
|
|
||||||
|
@ -11,11 +11,15 @@ import CrossIcon from '@icon/CrossIcon';
|
||||||
import useSubmit from '@hooks/useSubmit';
|
import useSubmit from '@hooks/useSubmit';
|
||||||
|
|
||||||
const ChatContent = () => {
|
const ChatContent = () => {
|
||||||
const [messages, inputRole, setError] = useStore((state) => [
|
const inputRole = useStore((state) => state.inputRole);
|
||||||
state.messages,
|
const setError = useStore((state) => state.setError);
|
||||||
state.inputRole,
|
const messages = useStore((state) =>
|
||||||
state.setError,
|
state.chats ? state.chats[state.currentChatIndex].messages : []
|
||||||
]);
|
);
|
||||||
|
const stickyIndex = useStore((state) =>
|
||||||
|
state.chats ? state.chats[state.currentChatIndex].messages.length : 0
|
||||||
|
);
|
||||||
|
|
||||||
const { handleSubmit, error } = useSubmit();
|
const { handleSubmit, error } = useSubmit();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -38,7 +42,12 @@ const ChatContent = () => {
|
||||||
<NewMessageButton messageIndex={index} />
|
<NewMessageButton messageIndex={index} />
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
<Message role={inputRole} content='' messageIndex={-1} sticky />
|
<Message
|
||||||
|
role={inputRole}
|
||||||
|
content=''
|
||||||
|
messageIndex={stickyIndex}
|
||||||
|
sticky
|
||||||
|
/>
|
||||||
|
|
||||||
{error !== '' && (
|
{error !== '' && (
|
||||||
<div className='relative bg-red-600/50 p-2 rounded-sm w-3/5 mt-3'>
|
<div className='relative bg-red-600/50 p-2 rounded-sm w-3/5 mt-3'>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import useStore from '@store/store';
|
|
||||||
|
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import MessageContent from './MessageContent';
|
import MessageContent from './MessageContent';
|
||||||
|
@ -14,50 +13,48 @@ import RoleSelector from './RoleSelector';
|
||||||
// };
|
// };
|
||||||
const backgroundStyle = ['dark:bg-gray-800', 'bg-gray-50 dark:bg-[#444654]'];
|
const backgroundStyle = ['dark:bg-gray-800', 'bg-gray-50 dark:bg-[#444654]'];
|
||||||
|
|
||||||
const Message = ({
|
const Message = React.memo(
|
||||||
role,
|
({
|
||||||
content,
|
role,
|
||||||
messageIndex,
|
content,
|
||||||
sticky = false,
|
messageIndex,
|
||||||
}: {
|
sticky = false,
|
||||||
role: Role;
|
}: {
|
||||||
content: string;
|
role: Role;
|
||||||
messageIndex: number;
|
content: string;
|
||||||
sticky?: boolean;
|
messageIndex: number;
|
||||||
}) => {
|
sticky?: boolean;
|
||||||
const stickyIndex = useStore((state) => state.messages.length);
|
}) => {
|
||||||
|
return (
|
||||||
return (
|
<div
|
||||||
<div
|
className={`w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group ${
|
||||||
className={`w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group ${
|
backgroundStyle[messageIndex % 2]
|
||||||
sticky
|
}`}
|
||||||
? backgroundStyle[stickyIndex % 2]
|
key={
|
||||||
: backgroundStyle[messageIndex % 2]
|
messageIndex !== -1
|
||||||
}`}
|
? `${messageIndex}-${content}`
|
||||||
key={
|
: 'sticky-message-text-area'
|
||||||
messageIndex !== -1
|
}
|
||||||
? `${messageIndex}-${content}`
|
>
|
||||||
: 'sticky-message-text-area'
|
<div className='text-base gap-4 md:gap-6 m-auto md:max-w-2xl lg:max-w-2xl xl:max-w-3xl p-4 md:py-6 flex lg:px-0'>
|
||||||
}
|
<Avatar role={role} />
|
||||||
>
|
<div className='w-[calc(100%-50px)] '>
|
||||||
<div className='text-base gap-4 md:gap-6 m-auto md:max-w-2xl lg:max-w-2xl xl:max-w-3xl p-4 md:py-6 flex lg:px-0'>
|
<RoleSelector
|
||||||
<Avatar role={role} />
|
role={role}
|
||||||
<div className='w-[calc(100%-50px)] '>
|
messageIndex={messageIndex}
|
||||||
<RoleSelector
|
sticky={sticky}
|
||||||
role={role}
|
/>
|
||||||
messageIndex={messageIndex}
|
<MessageContent
|
||||||
sticky={sticky}
|
role={role}
|
||||||
/>
|
content={content}
|
||||||
<MessageContent
|
messageIndex={messageIndex}
|
||||||
role={role}
|
sticky={sticky}
|
||||||
content={content}
|
/>
|
||||||
messageIndex={messageIndex}
|
</div>
|
||||||
sticky={sticky}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
};
|
);
|
||||||
|
|
||||||
export default Message;
|
export default Message;
|
||||||
|
|
|
@ -17,7 +17,7 @@ import DownChevronArrow from '@icon/DownChevronArrow';
|
||||||
|
|
||||||
import useSubmit from '@hooks/useSubmit';
|
import useSubmit from '@hooks/useSubmit';
|
||||||
|
|
||||||
import { MessageInterface } from '@type/chat';
|
import { ChatInterface } from '@type/chat';
|
||||||
|
|
||||||
import PopupModal from '@components/PopupModal';
|
import PopupModal from '@components/PopupModal';
|
||||||
|
|
||||||
|
@ -56,158 +56,168 @@ const MessageContent = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ContentView = ({
|
const ContentView = React.memo(
|
||||||
role,
|
({
|
||||||
content,
|
role,
|
||||||
setIsEdit,
|
content,
|
||||||
messageIndex,
|
setIsEdit,
|
||||||
}: {
|
messageIndex,
|
||||||
role: string;
|
}: {
|
||||||
content: string;
|
role: string;
|
||||||
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
|
content: string;
|
||||||
messageIndex: number;
|
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}) => {
|
messageIndex: number;
|
||||||
const { handleSubmit, error } = useSubmit();
|
}) => {
|
||||||
|
const { handleSubmit } = useSubmit();
|
||||||
|
|
||||||
const [isDelete, setIsDelete] = useState<boolean>(false);
|
const [isDelete, setIsDelete] = useState<boolean>(false);
|
||||||
const [copied, setCopied] = useState<boolean>(false);
|
const [copied, setCopied] = useState<boolean>(false);
|
||||||
|
const currentChatIndex = useStore((state) => state.currentChatIndex);
|
||||||
|
const setChats = useStore((state) => state.setChats);
|
||||||
|
const lastMessageIndex = useStore((state) =>
|
||||||
|
state.chats ? state.chats[state.currentChatIndex].messages.length - 1 : 0
|
||||||
|
);
|
||||||
|
|
||||||
const [messages, setMessages] = useStore((state) => [
|
const handleDelete = () => {
|
||||||
state.messages,
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
state.setMessages,
|
JSON.stringify(useStore.getState().chats)
|
||||||
]);
|
);
|
||||||
|
updatedChats[currentChatIndex].messages.splice(messageIndex, 1);
|
||||||
|
setChats(updatedChats);
|
||||||
|
};
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleMove = (direction: 'up' | 'down') => {
|
||||||
const updatedMessages = JSON.parse(JSON.stringify(messages));
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
updatedMessages.splice(messageIndex, 1);
|
JSON.stringify(useStore.getState().chats)
|
||||||
setMessages(updatedMessages);
|
);
|
||||||
};
|
const updatedMessages = updatedChats[currentChatIndex].messages;
|
||||||
|
const temp = updatedMessages[messageIndex];
|
||||||
|
if (direction === 'up') {
|
||||||
|
updatedMessages[messageIndex] = updatedMessages[messageIndex - 1];
|
||||||
|
updatedMessages[messageIndex - 1] = temp;
|
||||||
|
} else {
|
||||||
|
updatedMessages[messageIndex] = updatedMessages[messageIndex + 1];
|
||||||
|
updatedMessages[messageIndex + 1] = temp;
|
||||||
|
}
|
||||||
|
setChats(updatedChats);
|
||||||
|
};
|
||||||
|
|
||||||
const handleMove = (direction: 'up' | 'down') => {
|
const handleRefresh = () => {
|
||||||
const updatedMessages = JSON.parse(JSON.stringify(messages));
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
const temp = updatedMessages[messageIndex];
|
JSON.stringify(useStore.getState().chats)
|
||||||
if (direction === 'up') {
|
);
|
||||||
updatedMessages[messageIndex] = updatedMessages[messageIndex - 1];
|
const updatedMessages = updatedChats[currentChatIndex].messages;
|
||||||
updatedMessages[messageIndex - 1] = temp;
|
updatedMessages.splice(updatedMessages.length - 1, 1);
|
||||||
} else {
|
setChats(updatedChats);
|
||||||
updatedMessages[messageIndex] = updatedMessages[messageIndex + 1];
|
handleSubmit();
|
||||||
updatedMessages[messageIndex + 1] = temp;
|
};
|
||||||
}
|
|
||||||
setMessages(updatedMessages);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRefresh = () => {
|
return (
|
||||||
const updatedMessages = JSON.parse(JSON.stringify(messages));
|
<>
|
||||||
updatedMessages.splice(updatedMessages.length - 1, 1);
|
<div className='markdown prose w-full break-words dark:prose-invert dark'>
|
||||||
setMessages(updatedMessages);
|
<ReactMarkdown
|
||||||
handleSubmit();
|
remarkPlugins={[remarkMath, remarkGfm]}
|
||||||
};
|
rehypePlugins={[[rehypeKatex, { output: 'mathml' }]]}
|
||||||
|
components={{
|
||||||
|
code({ node, inline, className, children, ...props }) {
|
||||||
|
if (inline) return <code>{children}</code>;
|
||||||
|
let highlight;
|
||||||
|
|
||||||
return (
|
const match = /language-(\w+)/.exec(className || '');
|
||||||
<>
|
const lang = match && match[1];
|
||||||
<div className='markdown prose w-full break-words dark:prose-invert dark'>
|
if (lang)
|
||||||
<ReactMarkdown
|
highlight = hljs.highlight(children.toString(), {
|
||||||
remarkPlugins={[remarkMath, remarkGfm]}
|
language: lang,
|
||||||
rehypePlugins={[[rehypeKatex, { output: 'mathml' }]]}
|
});
|
||||||
components={{
|
else highlight = hljs.highlightAuto(children.toString());
|
||||||
code({ node, inline, className, children, ...props }) {
|
|
||||||
if (inline) return <code>{children}</code>;
|
|
||||||
let highlight;
|
|
||||||
|
|
||||||
const match = /language-(\w+)/.exec(className || '');
|
return (
|
||||||
const lang = match && match[1];
|
<div className='bg-black rounded-md'>
|
||||||
if (lang)
|
<div className='flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans'>
|
||||||
highlight = hljs.highlight(children.toString(), {
|
<span className=''>{highlight.language}</span>
|
||||||
language: lang,
|
<button
|
||||||
});
|
className='flex ml-auto gap-2'
|
||||||
else highlight = hljs.highlightAuto(children.toString());
|
onClick={() => {
|
||||||
|
navigator.clipboard
|
||||||
return (
|
.writeText(children.toString())
|
||||||
<div className='bg-black rounded-md'>
|
.then(() => {
|
||||||
<div className='flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans'>
|
setCopied(true);
|
||||||
<span className=''>{highlight.language}</span>
|
setTimeout(() => setCopied(false), 3000);
|
||||||
<button
|
});
|
||||||
className='flex ml-auto gap-2'
|
|
||||||
onClick={() => {
|
|
||||||
navigator.clipboard
|
|
||||||
.writeText(children.toString())
|
|
||||||
.then(() => {
|
|
||||||
setCopied(true);
|
|
||||||
setTimeout(() => setCopied(false), 3000);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{copied ? (
|
|
||||||
<>
|
|
||||||
<TickIcon />
|
|
||||||
Copied!
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<CopyIcon />
|
|
||||||
Copy code
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className='p-4 overflow-y-auto'>
|
|
||||||
<code
|
|
||||||
className={`!whitespace-pre hljs language-${highlight.language}`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: DOMPurify.sanitize(highlight.value, {
|
|
||||||
USE_PROFILES: { html: true },
|
|
||||||
}),
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</code>
|
{copied ? (
|
||||||
|
<>
|
||||||
|
<TickIcon />
|
||||||
|
Copied!
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<CopyIcon />
|
||||||
|
Copy code
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className='p-4 overflow-y-auto'>
|
||||||
|
<code
|
||||||
|
className={`!whitespace-pre hljs language-${highlight.language}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: DOMPurify.sanitize(highlight.value, {
|
||||||
|
USE_PROFILES: { html: true },
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
},
|
||||||
},
|
p({ className, children, ...props }) {
|
||||||
p({ className, children, ...props }) {
|
return <p className='whitespace-pre-wrap'>{children}</p>;
|
||||||
return <p className='whitespace-pre-wrap'>{children}</p>;
|
},
|
||||||
},
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{content}
|
||||||
{content}
|
</ReactMarkdown>
|
||||||
</ReactMarkdown>
|
</div>
|
||||||
</div>
|
<div className='flex justify-end gap-2 w-full mt-2'>
|
||||||
<div className='flex justify-end gap-2 w-full mt-2'>
|
{isDelete || (
|
||||||
{isDelete || (
|
<>
|
||||||
<>
|
{role === 'assistant' && messageIndex === lastMessageIndex && (
|
||||||
{role === 'assistant' && messageIndex === messages?.length - 1 && (
|
<RefreshButton onClick={handleRefresh} />
|
||||||
<RefreshButton onClick={handleRefresh} />
|
)}
|
||||||
)}
|
{messageIndex !== 0 && (
|
||||||
{messageIndex !== 0 && (
|
<UpButton onClick={() => handleMove('up')} />
|
||||||
<UpButton onClick={() => handleMove('up')} />
|
)}
|
||||||
)}
|
{messageIndex !== lastMessageIndex && (
|
||||||
{messageIndex !== messages?.length - 1 && (
|
<DownButton onClick={() => handleMove('down')} />
|
||||||
<DownButton onClick={() => handleMove('down')} />
|
)}
|
||||||
)}
|
|
||||||
|
|
||||||
<EditButton setIsEdit={setIsEdit} />
|
<EditButton setIsEdit={setIsEdit} />
|
||||||
<DeleteButton setIsDelete={setIsDelete} />
|
<DeleteButton setIsDelete={setIsDelete} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{isDelete && (
|
{isDelete && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className='p-1 hover:text-white'
|
className='p-1 hover:text-white'
|
||||||
onClick={() => setIsDelete(false)}
|
onClick={() => setIsDelete(false)}
|
||||||
>
|
>
|
||||||
<CrossIcon />
|
<CrossIcon />
|
||||||
</button>
|
</button>
|
||||||
<button className='p-1 hover:text-white' onClick={handleDelete}>
|
<button className='p-1 hover:text-white' onClick={handleDelete}>
|
||||||
<TickIcon />
|
<TickIcon />
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const MessageButton = ({
|
const MessageButton = ({
|
||||||
onClick,
|
onClick,
|
||||||
|
@ -286,11 +296,9 @@ const EditView = ({
|
||||||
messageIndex: number;
|
messageIndex: number;
|
||||||
sticky?: boolean;
|
sticky?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [messages, setMessages, inputRole] = useStore((state) => [
|
const inputRole = useStore((state) => state.inputRole);
|
||||||
state.messages,
|
const setChats = useStore((state) => state.setChats);
|
||||||
state.setMessages,
|
const currentChatIndex = useStore((state) => state.currentChatIndex);
|
||||||
state.inputRole,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [_content, _setContent] = useState<string>(content);
|
const [_content, _setContent] = useState<string>(content);
|
||||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||||
|
@ -319,9 +327,10 @@ const EditView = ({
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (_content === '') return;
|
if (_content === '') return;
|
||||||
const updatedMessages: MessageInterface[] = JSON.parse(
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
JSON.stringify(messages)
|
JSON.stringify(useStore.getState().chats)
|
||||||
);
|
);
|
||||||
|
const updatedMessages = updatedChats[currentChatIndex].messages;
|
||||||
if (sticky) {
|
if (sticky) {
|
||||||
updatedMessages.push({ role: inputRole, content: _content });
|
updatedMessages.push({ role: inputRole, content: _content });
|
||||||
_setContent('');
|
_setContent('');
|
||||||
|
@ -330,26 +339,29 @@ const EditView = ({
|
||||||
updatedMessages[messageIndex].content = _content;
|
updatedMessages[messageIndex].content = _content;
|
||||||
setIsEdit(false);
|
setIsEdit(false);
|
||||||
}
|
}
|
||||||
setMessages(updatedMessages);
|
setChats(updatedChats);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { handleSubmit } = useSubmit();
|
const { handleSubmit } = useSubmit();
|
||||||
const handleSaveAndSubmit = () => {
|
const handleSaveAndSubmit = () => {
|
||||||
if (_content == '') return;
|
if (_content == '') return;
|
||||||
const updatedMessages: MessageInterface[] = JSON.parse(
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
JSON.stringify(messages)
|
JSON.stringify(useStore.getState().chats)
|
||||||
);
|
);
|
||||||
|
const updatedMessages = updatedChats[currentChatIndex].messages;
|
||||||
if (sticky) {
|
if (sticky) {
|
||||||
updatedMessages.push({ role: inputRole, content: _content });
|
updatedMessages.push({ role: inputRole, content: _content });
|
||||||
_setContent('');
|
_setContent('');
|
||||||
setMessages(updatedMessages);
|
|
||||||
resetTextAreaHeight();
|
resetTextAreaHeight();
|
||||||
} else {
|
} else {
|
||||||
updatedMessages[messageIndex].content = _content;
|
updatedMessages[messageIndex].content = _content;
|
||||||
const _updatedMessages = updatedMessages.slice(0, messageIndex + 1);
|
updatedChats[currentChatIndex].messages = updatedMessages.slice(
|
||||||
setMessages(_updatedMessages);
|
0,
|
||||||
|
messageIndex + 1
|
||||||
|
);
|
||||||
setIsEdit(false);
|
setIsEdit(false);
|
||||||
}
|
}
|
||||||
|
setChats(updatedChats);
|
||||||
handleSubmit();
|
handleSubmit();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,72 +3,62 @@ import useStore from '@store/store';
|
||||||
|
|
||||||
import PlusIcon from '@icon/PlusIcon';
|
import PlusIcon from '@icon/PlusIcon';
|
||||||
|
|
||||||
import { ChatInterface, MessageInterface } from '@type/chat';
|
import { ChatInterface } from '@type/chat';
|
||||||
import { defaultSystemMessage } from '@constants/chat';
|
import { defaultSystemMessage } from '@constants/chat';
|
||||||
|
|
||||||
const NewMessageButton = ({ messageIndex }: { messageIndex: number }) => {
|
const NewMessageButton = React.memo(
|
||||||
const [
|
({ messageIndex }: { messageIndex: number }) => {
|
||||||
messages,
|
const setChats = useStore((state) => state.setChats);
|
||||||
chats,
|
const currentChatIndex = useStore((state) => state.currentChatIndex);
|
||||||
setMessages,
|
const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
|
||||||
currentChatIndex,
|
|
||||||
setChats,
|
|
||||||
setCurrentChatIndex,
|
|
||||||
] = useStore((state) => [
|
|
||||||
state.messages,
|
|
||||||
state.chats,
|
|
||||||
state.setMessages,
|
|
||||||
state.currentChatIndex,
|
|
||||||
state.setChats,
|
|
||||||
state.setCurrentChatIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const addChat = () => {
|
const addChat = () => {
|
||||||
if (chats) {
|
const chats = useStore.getState().chats;
|
||||||
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
if (chats) {
|
||||||
let titleIndex = 1;
|
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
||||||
let title = `New Chat ${titleIndex}`;
|
let titleIndex = 1;
|
||||||
|
let title = `New Chat ${titleIndex}`;
|
||||||
|
|
||||||
while (chats.some((chat) => chat.title === title)) {
|
while (chats.some((chat) => chat.title === title)) {
|
||||||
titleIndex += 1;
|
titleIndex += 1;
|
||||||
title = `New Chat ${titleIndex}`;
|
title = `New Chat ${titleIndex}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedChats.unshift({
|
||||||
|
title,
|
||||||
|
messages: [{ role: 'system', content: defaultSystemMessage }],
|
||||||
|
});
|
||||||
|
setChats(updatedChats);
|
||||||
|
setCurrentChatIndex(0);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
updatedChats.unshift({
|
const addMessage = () => {
|
||||||
title,
|
if (currentChatIndex === -1) {
|
||||||
messages: [{ role: 'system', content: defaultSystemMessage }],
|
addChat();
|
||||||
});
|
} else {
|
||||||
setChats(updatedChats);
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
setMessages(updatedChats[0].messages);
|
JSON.stringify(useStore.getState().chats)
|
||||||
setCurrentChatIndex(0);
|
);
|
||||||
}
|
updatedChats[currentChatIndex].messages.splice(messageIndex + 1, 0, {
|
||||||
};
|
content: '',
|
||||||
|
role: 'user',
|
||||||
|
});
|
||||||
|
setChats(updatedChats);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const addMessage = () => {
|
return (
|
||||||
if (currentChatIndex === -1) {
|
<div className='h-0 w-0 relative' key={messageIndex}>
|
||||||
addChat();
|
<div
|
||||||
} else {
|
className='absolute top-0 right-0 translate-x-1/2 translate-y-[-50%] text-gray-600 dark:text-white cursor-pointer bg-gray-200 dark:bg-gray-600/80 rounded-full p-1 text-sm hover:bg-gray-300 dark:hover:bg-gray-800/80 transition-bg duration-200'
|
||||||
const updatedMessages: MessageInterface[] = JSON.parse(
|
onClick={addMessage}
|
||||||
JSON.stringify(messages)
|
>
|
||||||
);
|
<PlusIcon />
|
||||||
updatedMessages.splice(messageIndex + 1, 0, {
|
</div>
|
||||||
content: '',
|
|
||||||
role: 'user',
|
|
||||||
});
|
|
||||||
setMessages(updatedMessages);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='h-0 w-0 relative' key={messageIndex}>
|
|
||||||
<div
|
|
||||||
className='absolute top-0 right-0 translate-x-1/2 translate-y-[-50%] text-gray-600 dark:text-white cursor-pointer bg-gray-200 dark:bg-gray-600/80 rounded-full p-1 text-sm hover:bg-gray-300 dark:hover:bg-gray-800/80 transition-bg duration-200'
|
|
||||||
onClick={addMessage}
|
|
||||||
>
|
|
||||||
<PlusIcon />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
};
|
);
|
||||||
|
|
||||||
export default NewMessageButton;
|
export default NewMessageButton;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||||
import useStore from '@store/store';
|
import useStore from '@store/store';
|
||||||
|
|
||||||
import DownChevronArrow from '@icon/DownChevronArrow';
|
import DownChevronArrow from '@icon/DownChevronArrow';
|
||||||
import { MessageInterface, Role, roles } from '@type/chat';
|
import { ChatInterface, Role, roles } from '@type/chat';
|
||||||
|
|
||||||
const RoleSelector = ({
|
const RoleSelector = ({
|
||||||
role,
|
role,
|
||||||
|
@ -13,11 +13,9 @@ const RoleSelector = ({
|
||||||
messageIndex: number;
|
messageIndex: number;
|
||||||
sticky?: boolean;
|
sticky?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [messages, setMessages, setInputRole] = useStore((state) => [
|
const setInputRole = useStore((state) => state.setInputRole);
|
||||||
state.messages,
|
const setChats = useStore((state) => state.setChats);
|
||||||
state.setMessages,
|
const currentChatIndex = useStore((state) => state.currentChatIndex);
|
||||||
state.setInputRole,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [dropDown, setDropDown] = useState<boolean>(false);
|
const [dropDown, setDropDown] = useState<boolean>(false);
|
||||||
|
|
||||||
|
@ -46,11 +44,12 @@ const RoleSelector = ({
|
||||||
className='px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white cursor-pointer'
|
className='px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white cursor-pointer'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!sticky) {
|
if (!sticky) {
|
||||||
const updatedMessages: MessageInterface[] = JSON.parse(
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
JSON.stringify(messages)
|
JSON.stringify(useStore.getState().chats)
|
||||||
);
|
);
|
||||||
updatedMessages[messageIndex].role = r;
|
updatedChats[currentChatIndex].messages[messageIndex].role =
|
||||||
setMessages(updatedMessages);
|
r;
|
||||||
|
setChats(updatedChats);
|
||||||
} else {
|
} else {
|
||||||
setInputRole(r);
|
setInputRole(r);
|
||||||
}
|
}
|
||||||
|
@ -66,5 +65,4 @@ const RoleSelector = ({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RoleSelector;
|
export default RoleSelector;
|
||||||
|
|
|
@ -5,15 +5,12 @@ import CrossIcon2 from '@icon/CrossIcon2';
|
||||||
import { validateApiKey } from '@api/customApi';
|
import { validateApiKey } from '@api/customApi';
|
||||||
|
|
||||||
const ConfigMenu = () => {
|
const ConfigMenu = () => {
|
||||||
const [apiKey, setApiKey, apiFree, setApiFree, openConfig, setOpenConfig] =
|
const apiKey = useStore((state) => state.apiKey);
|
||||||
useStore((state) => [
|
const setApiKey = useStore((state) => state.setApiKey);
|
||||||
state.apiKey,
|
const apiFree = useStore((state) => state.apiFree);
|
||||||
state.setApiKey,
|
const setApiFree = useStore((state) => state.setApiFree);
|
||||||
state.apiFree,
|
const openConfig = useStore((state) => state.openConfig);
|
||||||
state.setApiFree,
|
const setOpenConfig = useStore((state) => state.setOpenConfig);
|
||||||
state.openConfig,
|
|
||||||
state.setOpenConfig,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [_apiKey, _setApiKey] = useState<string>(apiKey || '');
|
const [_apiKey, _setApiKey] = useState<string>(apiKey || '');
|
||||||
const [error, setError] = useState<boolean>(false);
|
const [error, setError] = useState<boolean>(false);
|
||||||
|
|
|
@ -10,11 +10,8 @@ import CrossIcon from '@icon/CrossIcon';
|
||||||
import useInitialiseNewChat from '@hooks/useInitialiseNewChat';
|
import useInitialiseNewChat from '@hooks/useInitialiseNewChat';
|
||||||
|
|
||||||
const ChatHistoryList = () => {
|
const ChatHistoryList = () => {
|
||||||
const [chats, setCurrentChatIndex, setMessages] = useStore((state) => [
|
const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
|
||||||
state.chats,
|
const chats = useStore((state) => state.chats);
|
||||||
state.setCurrentChatIndex,
|
|
||||||
state.setMessages,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex-col flex-1 overflow-y-auto border-b border-white/20'>
|
<div className='flex-col flex-1 overflow-y-auto border-b border-white/20'>
|
||||||
|
@ -27,7 +24,6 @@ const ChatHistoryList = () => {
|
||||||
chatIndex={index}
|
chatIndex={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentChatIndex(index);
|
setCurrentChatIndex(index);
|
||||||
setMessages(chats[index].messages);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
@ -66,14 +62,9 @@ const ChatHistory = ({
|
||||||
onClick?: React.MouseEventHandler<HTMLElement>;
|
onClick?: React.MouseEventHandler<HTMLElement>;
|
||||||
}) => {
|
}) => {
|
||||||
const initialiseNewChat = useInitialiseNewChat();
|
const initialiseNewChat = useInitialiseNewChat();
|
||||||
const [chats, setChats, currentChatIndex, setMessages, setCurrentChatIndex] =
|
const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
|
||||||
useStore((state) => [
|
const setChats = useStore((state) => state.setChats);
|
||||||
state.chats,
|
const currentChatIndex = useStore((state) => state.currentChatIndex);
|
||||||
state.setChats,
|
|
||||||
state.currentChatIndex,
|
|
||||||
state.setMessages,
|
|
||||||
state.setCurrentChatIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [isDelete, setIsDelete] = useState<boolean>(false);
|
const [isDelete, setIsDelete] = useState<boolean>(false);
|
||||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||||
|
@ -83,7 +74,7 @@ const ChatHistory = ({
|
||||||
|
|
||||||
const handleTick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleTick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const updatedChats = JSON.parse(JSON.stringify(chats));
|
const updatedChats = JSON.parse(JSON.stringify(useStore.getState().chats));
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
updatedChats[chatIndex].title = _title;
|
updatedChats[chatIndex].title = _title;
|
||||||
setChats(updatedChats);
|
setChats(updatedChats);
|
||||||
|
@ -92,7 +83,6 @@ const ChatHistory = ({
|
||||||
updatedChats.splice(chatIndex, 1);
|
updatedChats.splice(chatIndex, 1);
|
||||||
if (updatedChats.length > 0) {
|
if (updatedChats.length > 0) {
|
||||||
setCurrentChatIndex(0);
|
setCurrentChatIndex(0);
|
||||||
setMessages(updatedChats[0].messages);
|
|
||||||
setChats(updatedChats);
|
setChats(updatedChats);
|
||||||
} else {
|
} else {
|
||||||
initialiseNewChat();
|
initialiseNewChat();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import useStore from '@store/store';
|
|
||||||
|
|
||||||
import NewChat from './NewChat';
|
import NewChat from './NewChat';
|
||||||
import ChatHistoryList from './ChatHistoryList';
|
import ChatHistoryList from './ChatHistoryList';
|
||||||
|
|
|
@ -4,10 +4,8 @@ import useStore from '@store/store';
|
||||||
import PersonIcon from '@icon/PersonIcon';
|
import PersonIcon from '@icon/PersonIcon';
|
||||||
|
|
||||||
const Config = () => {
|
const Config = () => {
|
||||||
const [apiFree, setOpenConfig] = useStore((state) => [
|
const apiFree = useStore((state) => state.apiFree);
|
||||||
state.apiFree,
|
const setOpenConfig = useStore((state) => state.setOpenConfig);
|
||||||
state.setOpenConfig,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -1,20 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import useStore from '@store/store';
|
|
||||||
|
|
||||||
import PlusIcon from '@icon/PlusIcon';
|
import PlusIcon from '@icon/PlusIcon';
|
||||||
|
|
||||||
import useAddChat from '@hooks/useAddChat';
|
import useAddChat from '@hooks/useAddChat';
|
||||||
|
|
||||||
const NewChat = () => {
|
const NewChat = () => {
|
||||||
const [chats, setChats, setCurrentChatIndex, setMessages] = useStore(
|
|
||||||
(state) => [
|
|
||||||
state.chats,
|
|
||||||
state.setChats,
|
|
||||||
state.setCurrentChatIndex,
|
|
||||||
state.setMessages,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
const addChat = useAddChat();
|
const addChat = useAddChat();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,10 +5,12 @@ import PlusIcon from '@icon/PlusIcon';
|
||||||
import useAddChat from '@hooks/useAddChat';
|
import useAddChat from '@hooks/useAddChat';
|
||||||
|
|
||||||
const MobileBar = () => {
|
const MobileBar = () => {
|
||||||
const [chats, currentChatIndex] = useStore((state) => [
|
const chatTitle = useStore((state) =>
|
||||||
state.chats,
|
state.chats && state.chats.length > 0
|
||||||
state.currentChatIndex,
|
? state.chats[state.currentChatIndex].title
|
||||||
]);
|
: 'New Chat'
|
||||||
|
);
|
||||||
|
|
||||||
const addChat = useAddChat();
|
const addChat = useAddChat();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -42,11 +44,7 @@ const MobileBar = () => {
|
||||||
<line x1='3' y1='18' x2='21' y2='18'></line>
|
<line x1='3' y1='18' x2='21' y2='18'></line>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<h1 className='flex-1 text-center text-base font-normal'>
|
<h1 className='flex-1 text-center text-base font-normal'>{chatTitle}</h1>
|
||||||
{chats && chats.length > 0
|
|
||||||
? chats[currentChatIndex]?.title
|
|
||||||
: 'New Chat'}
|
|
||||||
</h1>
|
|
||||||
<button type='button' className='px-3 text-gray-400' onClick={addChat}>
|
<button type='button' className='px-3 text-gray-400' onClick={addChat}>
|
||||||
<PlusIcon className='h-6 w-6' />
|
<PlusIcon className='h-6 w-6' />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -4,16 +4,11 @@ import { defaultSystemMessage } from '@constants/chat';
|
||||||
import { ChatInterface } from '@type/chat';
|
import { ChatInterface } from '@type/chat';
|
||||||
|
|
||||||
const useAddChat = () => {
|
const useAddChat = () => {
|
||||||
const [chats, setChats, setCurrentChatIndex, setMessages] = useStore(
|
const setChats = useStore((state) => state.setChats);
|
||||||
(state) => [
|
const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
|
||||||
state.chats,
|
|
||||||
state.setChats,
|
|
||||||
state.setCurrentChatIndex,
|
|
||||||
state.setMessages,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
const addChat = () => {
|
const addChat = () => {
|
||||||
|
const chats = useStore.getState().chats;
|
||||||
if (chats) {
|
if (chats) {
|
||||||
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
||||||
let titleIndex = 1;
|
let titleIndex = 1;
|
||||||
|
@ -29,7 +24,6 @@ const useAddChat = () => {
|
||||||
messages: [{ role: 'system', content: defaultSystemMessage }],
|
messages: [{ role: 'system', content: defaultSystemMessage }],
|
||||||
});
|
});
|
||||||
setChats(updatedChats);
|
setChats(updatedChats);
|
||||||
setMessages(updatedChats[0].messages);
|
|
||||||
setCurrentChatIndex(0);
|
setCurrentChatIndex(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,11 +4,8 @@ import { MessageInterface } from '@type/chat';
|
||||||
import { defaultSystemMessage } from '@constants/chat';
|
import { defaultSystemMessage } from '@constants/chat';
|
||||||
|
|
||||||
const useInitialiseNewChat = () => {
|
const useInitialiseNewChat = () => {
|
||||||
const [setChats, setMessages, setCurrentChatIndex] = useStore((state) => [
|
const setChats = useStore((state) => state.setChats);
|
||||||
state.setChats,
|
const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
|
||||||
state.setMessages,
|
|
||||||
state.setCurrentChatIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const initialiseNewChat = () => {
|
const initialiseNewChat = () => {
|
||||||
const message: MessageInterface = {
|
const message: MessageInterface = {
|
||||||
|
@ -21,7 +18,6 @@ const useInitialiseNewChat = () => {
|
||||||
messages: [message],
|
messages: [message],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
setMessages([message]);
|
|
||||||
setCurrentChatIndex(0);
|
setCurrentChatIndex(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import useStore from '@store/store';
|
import useStore from '@store/store';
|
||||||
|
|
||||||
const useSaveToLocalStorage = () => {
|
const useSaveToLocalStorage = () => {
|
||||||
const chats = useStore((state) => state.chats);
|
const chatsRef = useRef(useStore.getState().chats);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chats) localStorage.setItem('chats', JSON.stringify(chats));
|
const unsubscribe = useStore.subscribe((state) => {
|
||||||
}, [chats]);
|
if (chatsRef && chatsRef.current !== state.chats) {
|
||||||
|
chatsRef.current = state.chats;
|
||||||
|
localStorage.setItem('chats', JSON.stringify(state.chats));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return unsubscribe;
|
||||||
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useSaveToLocalStorage;
|
export default useSaveToLocalStorage;
|
||||||
|
|
|
@ -1,48 +1,45 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import useStore from '@store/store';
|
import useStore from '@store/store';
|
||||||
import { MessageInterface } from '@type/chat';
|
import { ChatInterface } from '@type/chat';
|
||||||
import { getChatCompletionStream as getChatCompletionStreamFree } from '@api/freeApi';
|
import { getChatCompletionStream as getChatCompletionStreamFree } from '@api/freeApi';
|
||||||
import { getChatCompletionStream as getChatCompletionStreamCustom } from '@api/customApi';
|
import { getChatCompletionStream as getChatCompletionStreamCustom } from '@api/customApi';
|
||||||
import { parseEventSource } from '@api/helper';
|
import { parseEventSource } from '@api/helper';
|
||||||
|
|
||||||
const useSubmit = () => {
|
const useSubmit = () => {
|
||||||
const [
|
const error = useStore((state) => state.error);
|
||||||
error,
|
const setError = useStore((state) => state.setError);
|
||||||
setError,
|
const apiFree = useStore((state) => state.apiFree);
|
||||||
apiFree,
|
const apiKey = useStore((state) => state.apiKey);
|
||||||
apiKey,
|
const setGenerating = useStore((state) => state.setGenerating);
|
||||||
setMessages,
|
const generating = useStore((state) => state.generating);
|
||||||
setGenerating,
|
const currentChatIndex = useStore((state) => state.currentChatIndex);
|
||||||
generating,
|
const setChats = useStore((state) => state.setChats);
|
||||||
] = useStore((state) => [
|
|
||||||
state.error,
|
|
||||||
state.setError,
|
|
||||||
state.apiFree,
|
|
||||||
state.apiKey,
|
|
||||||
state.setMessages,
|
|
||||||
state.setGenerating,
|
|
||||||
state.generating,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (generating) return;
|
const chats = useStore.getState().chats;
|
||||||
const messages = useStore.getState().messages;
|
if (generating || !chats) return;
|
||||||
|
|
||||||
const updatedMessages: MessageInterface[] = JSON.parse(
|
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
||||||
JSON.stringify(messages)
|
|
||||||
);
|
|
||||||
|
|
||||||
updatedMessages.push({ role: 'assistant', content: '' });
|
updatedChats[currentChatIndex].messages.push({
|
||||||
|
role: 'assistant',
|
||||||
|
content: '',
|
||||||
|
});
|
||||||
|
|
||||||
setMessages(updatedMessages);
|
setChats(updatedChats);
|
||||||
setGenerating(true);
|
setGenerating(true);
|
||||||
let stream;
|
let stream;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (apiFree) {
|
if (apiFree) {
|
||||||
stream = await getChatCompletionStreamFree(messages);
|
stream = await getChatCompletionStreamFree(
|
||||||
|
chats[currentChatIndex].messages
|
||||||
|
);
|
||||||
} else if (apiKey) {
|
} else if (apiKey) {
|
||||||
stream = await getChatCompletionStreamCustom(apiKey, messages);
|
stream = await getChatCompletionStreamCustom(
|
||||||
|
apiKey,
|
||||||
|
chats[currentChatIndex].messages
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
|
@ -65,11 +62,12 @@ const useSubmit = () => {
|
||||||
}
|
}
|
||||||
}, '');
|
}, '');
|
||||||
|
|
||||||
const updatedMessages: MessageInterface[] = JSON.parse(
|
const updatedChats: ChatInterface[] = JSON.parse(
|
||||||
JSON.stringify(useStore.getState().messages)
|
JSON.stringify(useStore.getState().chats)
|
||||||
);
|
);
|
||||||
|
const updatedMessages = updatedChats[currentChatIndex].messages;
|
||||||
updatedMessages[updatedMessages.length - 1].content += resultString;
|
updatedMessages[updatedMessages.length - 1].content += resultString;
|
||||||
setMessages(updatedMessages);
|
setChats(updatedChats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import useStore from '@store/store';
|
|
||||||
import { ChatInterface, MessageInterface } from '@type/chat';
|
|
||||||
|
|
||||||
const useUpdateCharts = () => {
|
|
||||||
const [chats, messages, setChats, currentChatIndex] = useStore((state) => [
|
|
||||||
state.chats,
|
|
||||||
state.messages,
|
|
||||||
state.setChats,
|
|
||||||
state.currentChatIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentChatIndex !== -1 && chats && chats.length > 0) {
|
|
||||||
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
|
|
||||||
const updatedMessages: MessageInterface[] = JSON.parse(
|
|
||||||
JSON.stringify(messages)
|
|
||||||
);
|
|
||||||
updatedChats[currentChatIndex].messages = updatedMessages;
|
|
||||||
setChats(updatedChats);
|
|
||||||
}
|
|
||||||
}, [messages]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useUpdateCharts;
|
|
Loading…
Reference in a new issue