mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
adds dark mode and logs errors in message response
This commit is contained in:
parent
58498ed951
commit
b5042a738e
13 changed files with 13873 additions and 536 deletions
5
index.js
5
index.js
|
|
@ -4,6 +4,7 @@ import { createRoot } from 'react-dom/client';
|
|||
import { Provider } from 'react-redux';
|
||||
import { store } from './src/store';
|
||||
import App from './src/App';
|
||||
import { ThemeProvider } from './src/hooks/ThemeContext';
|
||||
import './src/style.css';
|
||||
|
||||
const container = document.getElementById('root');
|
||||
|
|
@ -11,6 +12,8 @@ const root = createRoot(container); // createRoot(container!) if you use TypeScr
|
|||
// reactDom.render(<App />, document.getElementById('root'));
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
<ThemeProvider>
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
);
|
||||
|
|
|
|||
14240
package-lock.json
generated
14240
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="utf-8" />
|
||||
<title>ChatGPT Clone</title>
|
||||
<link rel="shortcut icon" href="#">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"><script defer src="main.js"></script></head>
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
href="#"
|
||||
/>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1"
|
||||
/>
|
||||
<script
|
||||
defer
|
||||
src="main.js"
|
||||
></script>
|
||||
<!-- <script>
|
||||
// It's best to inline this in `head` to avoid FOUC (flash of unstyled content) when changing pages or themes
|
||||
if (
|
||||
localStorage.getItem('color-theme') === 'dark' ||
|
||||
(!('color-theme' in localStorage) &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
</script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="main.js"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -19,14 +19,15 @@ const App = () => {
|
|||
<Nav conversations={data} />
|
||||
{/* <div className="flex h-full flex-1 flex-col md:pl-[260px]"> */}
|
||||
<div className="flex h-full w-full flex-1 flex-col bg-gray-50 md:pl-[260px]">
|
||||
{/* <main className="relative h-full w-full transition-width flex flex-col overflow-hidden items-stretch flex-1"> */}
|
||||
<div className="relative h-full w-full transition-width flex flex-col overflow-hidden items-stretch flex-1">
|
||||
|
||||
<MobileNav />
|
||||
<Messages messages={messages} />
|
||||
<TextChat
|
||||
messages={messages}
|
||||
reloadConvos={mutate}
|
||||
/>
|
||||
{/* </main> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import NavLink from './NavLink';
|
||||
import TrashIcon from '../svg/TrashIcon';
|
||||
import manualSWR from '~/utils/fetchers';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
|
|
|||
19
src/components/Nav/DarkMode.jsx
Normal file
19
src/components/Nav/DarkMode.jsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import React, { useState, useContext } from 'react';
|
||||
import DarkModeIcon from '../svg/DarkModeIcon';
|
||||
import { ThemeContext } from '~/hooks/ThemeContext';
|
||||
|
||||
export default function DarkMode() {
|
||||
const { theme, setTheme } = useContext(ThemeContext);
|
||||
|
||||
const clickHandler = () => setTheme(theme === 'dark' ? 'light' : 'dark');
|
||||
|
||||
return (
|
||||
<a
|
||||
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"
|
||||
onClick={clickHandler}
|
||||
>
|
||||
<DarkModeIcon />
|
||||
Dark mode
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,17 +1,14 @@
|
|||
import React from 'react';
|
||||
import ClearConvos from './ClearConvos';
|
||||
import NavLink from './NavLink';
|
||||
import DarkModeIcon from '../svg/DarkModeIcon';
|
||||
import LogOutIcon from '../svg/LogOutIcon';
|
||||
import ClearConvos from './ClearConvos';
|
||||
import DarkMode from './DarkMode';
|
||||
|
||||
export default function NavLinks() {
|
||||
return (
|
||||
<>
|
||||
<ClearConvos />
|
||||
<NavLink
|
||||
svg={DarkModeIcon}
|
||||
text="Dark mode"
|
||||
/>
|
||||
<DarkMode />
|
||||
<NavLink
|
||||
svg={LogOutIcon}
|
||||
text="Log out"
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ export default function Message({ sender, text, last = false, error = false }) {
|
|||
const { isSubmitting } = useSelector((state) => state.submit);
|
||||
const props = {
|
||||
className:
|
||||
'group w-full border-b border-black/10 text-gray-800 dark:border-gray-900/50 dark:bg-gray-800 dark:text-gray-100'
|
||||
'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group dark:bg-gray-800'
|
||||
};
|
||||
|
||||
if (sender === 'GPT') {
|
||||
props.className =
|
||||
'group w-full border-b border-black/10 bg-gray-100 text-gray-800 dark:border-gray-900/50 dark:bg-[#444654] dark:text-gray-100';
|
||||
'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group bg-gray-50 dark:bg-[#444654]';
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ import Message from './Message';
|
|||
import Landing from './Landing';
|
||||
|
||||
export default function Messages({ messages }) {
|
||||
|
||||
if (messages.length === 0) {
|
||||
return <Landing />
|
||||
};
|
||||
return <Landing />;
|
||||
}
|
||||
|
||||
const messagesEndRef = useRef(null);
|
||||
|
||||
|
|
@ -25,17 +24,22 @@ export default function Messages({ messages }) {
|
|||
// </div>
|
||||
// <div className="flex h-full text-sm dark:bg-gray-800"></div>;
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto ">
|
||||
{messages.map((message, i) => (
|
||||
<Message
|
||||
key={i}
|
||||
sender={message.sender}
|
||||
text={message.text}
|
||||
last={i === messages.length - 1}
|
||||
error={!!message.error ? true : false}
|
||||
/>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
// <div className="flex-1 overflow-y-auto ">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="h-full dark:bg-gray-800">
|
||||
<div className="flex h-full flex-col items-center text-sm dark:bg-gray-800">
|
||||
{messages.map((message, i) => (
|
||||
<Message
|
||||
key={i}
|
||||
sender={message.sender}
|
||||
text={message.text}
|
||||
last={i === messages.length - 1}
|
||||
error={!!message.error ? true : false}
|
||||
/>
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ export default function TextChat({ messages, reloadConvos }) {
|
|||
dispatch(setSubmitState(false));
|
||||
};
|
||||
|
||||
const errorHandler = (data) => {
|
||||
console.log('Error:', data);
|
||||
const errorHandler = (event) => {
|
||||
console.log('Error:', event);
|
||||
const errorResponse = {
|
||||
...initialResponse,
|
||||
text: 'An error occurred. Please try again in a few moments.',
|
||||
text: `An error occurred. Please try again in a few moments.\n\nError message: ${event.data}`,
|
||||
error: true
|
||||
};
|
||||
dispatch(setSubmitState(false));
|
||||
|
|
@ -80,7 +80,7 @@ export default function TextChat({ messages, reloadConvos }) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="md:bg-vert-light-gradient dark:md:bg-vert-dark-gradient w-full border-t bg-white dark:border-white/20 dark:bg-gray-800 md:border-t-0 md:border-transparent md:!bg-transparent md:dark:border-transparent">
|
||||
<div className="md:bg-vert-light-gradient dark:md:bg-vert-dark-gradient absolute bottom-0 left-0 w-full border-t bg-white dark:border-white/20 dark:bg-gray-800 md:border-t-0 md:border-transparent md:!bg-transparent md:dark:border-transparent">
|
||||
<form className="stretch mx-2 flex flex-row gap-3 pt-2 last:mb-2 md:last:mb-6 lg:mx-auto lg:max-w-3xl lg:pt-6">
|
||||
<div className="relative flex h-full flex-1 md:flex-col">
|
||||
<div className="ml-1 mt-1.5 flex justify-center gap-0 md:m-auto md:mb-2 md:w-full md:gap-2" />
|
||||
|
|
|
|||
45
src/hooks/ThemeContext.js
Normal file
45
src/hooks/ThemeContext.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//ThemeContext.js
|
||||
|
||||
import React, { createContext, useState, useEffect } from 'react';
|
||||
|
||||
const getInitialTheme = () => {
|
||||
if (typeof window !== 'undefined' && window.localStorage) {
|
||||
const storedPrefs = window.localStorage.getItem('color-theme');
|
||||
if (typeof storedPrefs === 'string') {
|
||||
return storedPrefs;
|
||||
}
|
||||
|
||||
const userMedia = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
if (userMedia.matches) {
|
||||
return 'dark';
|
||||
}
|
||||
}
|
||||
|
||||
return 'light'; // light theme as the default;
|
||||
};
|
||||
|
||||
export const ThemeContext = createContext();
|
||||
|
||||
export const ThemeProvider = ({ initialTheme, children }) => {
|
||||
const [theme, setTheme] = useState(getInitialTheme);
|
||||
|
||||
const rawSetTheme = (rawTheme) => {
|
||||
const root = window.document.documentElement;
|
||||
const isDark = rawTheme === 'dark';
|
||||
|
||||
root.classList.remove(isDark ? 'light' : 'dark');
|
||||
root.classList.add(rawTheme);
|
||||
|
||||
localStorage.setItem('color-theme', rawTheme);
|
||||
};
|
||||
|
||||
if (initialTheme) {
|
||||
rawSetTheme(initialTheme);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
rawSetTheme(theme);
|
||||
}, [theme]);
|
||||
|
||||
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
|
||||
};
|
||||
|
|
@ -38,7 +38,7 @@ export default function handleSubmit({
|
|||
};
|
||||
|
||||
events.onerror = function (e) {
|
||||
console.log(e, 'error in opening conn.');
|
||||
console.log('error in opening conn.');
|
||||
events.close();
|
||||
errorHandler(e);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
// plugins: [require('flowbite/plugin')]
|
||||
plugins: []
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue