Merge branch 'main' of https://github.com/mckaywrigley/chatbot-ui-pro
This commit is contained in:
commit
1a9a7dcad0
|
@ -1,4 +1,4 @@
|
||||||
import { IconArrowDown, IconClearAll, IconSettings } from '@tabler/icons-react';
|
import { IconClearAll, IconSettings } from '@tabler/icons-react';
|
||||||
import {
|
import {
|
||||||
MutableRefObject,
|
MutableRefObject,
|
||||||
memo,
|
memo,
|
||||||
|
@ -467,6 +467,11 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
|
||||||
key={index}
|
key={index}
|
||||||
message={message}
|
message={message}
|
||||||
messageIndex={index}
|
messageIndex={index}
|
||||||
|
onEdit={(editedMessage) => {
|
||||||
|
setCurrentMessage(editedMessage);
|
||||||
|
// discard edited message and the ones that come after then resend
|
||||||
|
handleSend(editedMessage, selectedConversation?.messages.length - index);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
@ -487,24 +492,16 @@ export const Chat = memo(({ stopConversationRef }: Props) => {
|
||||||
setCurrentMessage(message);
|
setCurrentMessage(message);
|
||||||
handleSend(message, 0, plugin);
|
handleSend(message, 0, plugin);
|
||||||
}}
|
}}
|
||||||
|
onScrollDownClick={handleScrollDown}
|
||||||
onRegenerate={() => {
|
onRegenerate={() => {
|
||||||
if (currentMessage) {
|
if (currentMessage) {
|
||||||
handleSend(currentMessage, 2, null);
|
handleSend(currentMessage, 2, null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
showScrollDownButton={showScrollDownButton}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{showScrollDownButton && (
|
|
||||||
<div className="absolute bottom-0 right-0 mb-4 mr-4 pb-20">
|
|
||||||
<button
|
|
||||||
className="flex h-7 w-7 items-center justify-center rounded-full bg-neutral-300 text-gray-800 shadow-md hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-neutral-200"
|
|
||||||
onClick={handleScrollDown}
|
|
||||||
>
|
|
||||||
<IconArrowDown size={18} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
IconArrowDown,
|
||||||
IconBolt,
|
IconBolt,
|
||||||
IconBrandGoogle,
|
IconBrandGoogle,
|
||||||
IconPlayerStop,
|
IconPlayerStop,
|
||||||
|
@ -30,15 +31,19 @@ import { VariableModal } from './VariableModal';
|
||||||
interface Props {
|
interface Props {
|
||||||
onSend: (message: Message, plugin: Plugin | null) => void;
|
onSend: (message: Message, plugin: Plugin | null) => void;
|
||||||
onRegenerate: () => void;
|
onRegenerate: () => void;
|
||||||
|
onScrollDownClick: () => void;
|
||||||
stopConversationRef: MutableRefObject<boolean>;
|
stopConversationRef: MutableRefObject<boolean>;
|
||||||
textareaRef: MutableRefObject<HTMLTextAreaElement | null>;
|
textareaRef: MutableRefObject<HTMLTextAreaElement | null>;
|
||||||
|
showScrollDownButton: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatInput = ({
|
export const ChatInput = ({
|
||||||
onSend,
|
onSend,
|
||||||
onRegenerate,
|
onRegenerate,
|
||||||
|
onScrollDownClick,
|
||||||
stopConversationRef,
|
stopConversationRef,
|
||||||
textareaRef,
|
textareaRef,
|
||||||
|
showScrollDownButton
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation('chat');
|
const { t } = useTranslation('chat');
|
||||||
|
|
||||||
|
@ -341,6 +346,17 @@ export const ChatInput = ({
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{showScrollDownButton && (
|
||||||
|
<div className="absolute bottom-12 right-0 lg:bottom-0 lg:-right-10">
|
||||||
|
<button
|
||||||
|
className="flex h-7 w-7 items-center justify-center rounded-full bg-neutral-300 text-gray-800 shadow-md hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-neutral-200"
|
||||||
|
onClick={onScrollDownClick}
|
||||||
|
>
|
||||||
|
<IconArrowDown size={18} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{showPromptList && filteredPrompts.length > 0 && (
|
{showPromptList && filteredPrompts.length > 0 && (
|
||||||
<div className="absolute bottom-12 w-full">
|
<div className="absolute bottom-12 w-full">
|
||||||
<PromptList
|
<PromptList
|
||||||
|
|
|
@ -26,9 +26,10 @@ import remarkMath from 'remark-math';
|
||||||
interface Props {
|
interface Props {
|
||||||
message: Message;
|
message: Message;
|
||||||
messageIndex: number;
|
messageIndex: number;
|
||||||
|
onEdit?: (editedMessage: Message) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatMessage: FC<Props> = memo(({ message, messageIndex }) => {
|
export const ChatMessage: FC<Props> = memo(({ message, messageIndex, onEdit }) => {
|
||||||
const { t } = useTranslation('chat');
|
const { t } = useTranslation('chat');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -57,31 +58,8 @@ export const ChatMessage: FC<Props> = memo(({ message, messageIndex }) => {
|
||||||
|
|
||||||
const handleEditMessage = () => {
|
const handleEditMessage = () => {
|
||||||
if (message.content != messageContent) {
|
if (message.content != messageContent) {
|
||||||
if (selectedConversation) {
|
if (selectedConversation && onEdit) {
|
||||||
const updatedMessages = selectedConversation.messages
|
onEdit({ ...message, content: messageContent });
|
||||||
.map((m, i) => {
|
|
||||||
if (i < messageIndex) {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter((m) => m) as Message[];
|
|
||||||
|
|
||||||
const updatedConversation = {
|
|
||||||
...selectedConversation,
|
|
||||||
messages: updatedMessages,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { single, all } = updateConversation(
|
|
||||||
updatedConversation,
|
|
||||||
conversations,
|
|
||||||
);
|
|
||||||
|
|
||||||
homeDispatch({ field: 'selectedConversation', value: single });
|
|
||||||
homeDispatch({ field: 'conversations', value: all });
|
|
||||||
homeDispatch({
|
|
||||||
field: 'currentMessage',
|
|
||||||
value: { ...message, content: messageContent },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { FC, useState } from 'react';
|
import { FC, useContext, useState } from 'react';
|
||||||
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { DEFAULT_TEMPERATURE } from '@/utils/app/const';
|
import { DEFAULT_TEMPERATURE } from '@/utils/app/const';
|
||||||
|
|
||||||
|
import HomeContext from '@/pages/api/home/home.context';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
label: string;
|
label: string;
|
||||||
onChangeTemperature: (temperature: number) => void;
|
onChangeTemperature: (temperature: number) => void;
|
||||||
|
@ -13,7 +15,13 @@ export const TemperatureSlider: FC<Props> = ({
|
||||||
label,
|
label,
|
||||||
onChangeTemperature,
|
onChangeTemperature,
|
||||||
}) => {
|
}) => {
|
||||||
const [temperature, setTemperature] = useState(DEFAULT_TEMPERATURE);
|
const {
|
||||||
|
state: { conversations },
|
||||||
|
} = useContext(HomeContext);
|
||||||
|
const lastConversation = conversations[conversations.length - 1];
|
||||||
|
const [temperature, setTemperature] = useState(
|
||||||
|
lastConversation?.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
|
);
|
||||||
const { t } = useTranslation('chat');
|
const { t } = useTranslation('chat');
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const newValue = parseFloat(event.target.value);
|
const newValue = parseFloat(event.target.value);
|
||||||
|
@ -31,7 +39,9 @@ export const TemperatureSlider: FC<Props> = ({
|
||||||
'Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.',
|
'Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.',
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="mt-2 mb-1 text-center">{temperature.toFixed(1)}</span>
|
<span className="mt-2 mb-1 text-center text-neutral-900 dark:text-neutral-100">
|
||||||
|
{temperature.toFixed(1)}
|
||||||
|
</span>
|
||||||
<input
|
<input
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
type="range"
|
type="range"
|
||||||
|
@ -41,7 +51,7 @@ export const TemperatureSlider: FC<Props> = ({
|
||||||
value={temperature}
|
value={temperature}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
<ul className="w mt-2 pb-8 flex justify-between px-[24px]">
|
<ul className="w mt-2 pb-8 flex justify-between px-[24px] text-neutral-900 dark:text-neutral-100">
|
||||||
<li className="relative flex justify-center">
|
<li className="relative flex justify-center">
|
||||||
<span className="absolute">{t('Precise')}</span>
|
<span className="absolute">{t('Precise')}</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import { IconFileExport, IconMoon, IconSun } from '@tabler/icons-react';
|
import {
|
||||||
import { useContext } from 'react';
|
IconFileExport,
|
||||||
|
IconMoon,
|
||||||
|
IconSettings,
|
||||||
|
IconSun,
|
||||||
|
} from '@tabler/icons-react';
|
||||||
|
import { useContext, useState } from 'react';
|
||||||
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import HomeContext from '@/pages/api/home/home.context';
|
import HomeContext from '@/pages/api/home/home.context';
|
||||||
|
|
||||||
|
import { SettingDialog } from '@/components/Settings/SettingDialog';
|
||||||
|
|
||||||
import { Import } from '../../Settings/Import';
|
import { Import } from '../../Settings/Import';
|
||||||
import { Key } from '../../Settings/Key';
|
import { Key } from '../../Settings/Key';
|
||||||
import { SidebarButton } from '../../Sidebar/SidebarButton';
|
import { SidebarButton } from '../../Sidebar/SidebarButton';
|
||||||
|
@ -14,6 +21,7 @@ import { PluginKeys } from './PluginKeys';
|
||||||
|
|
||||||
export const ChatbarSettings = () => {
|
export const ChatbarSettings = () => {
|
||||||
const { t } = useTranslation('sidebar');
|
const { t } = useTranslation('sidebar');
|
||||||
|
const [isSettingDialogOpen, setIsSettingDialog] = useState<boolean>(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: {
|
state: {
|
||||||
|
@ -49,16 +57,9 @@ export const ChatbarSettings = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SidebarButton
|
<SidebarButton
|
||||||
text={lightMode === 'light' ? t('Dark mode') : t('Light mode')}
|
text={t('Settings')}
|
||||||
icon={
|
icon={<IconSettings size={18} />}
|
||||||
lightMode === 'light' ? <IconMoon size={18} /> : <IconSun size={18} />
|
onClick={() => setIsSettingDialog(true)}
|
||||||
}
|
|
||||||
onClick={() =>
|
|
||||||
homeDispatch({
|
|
||||||
field: 'lightMode',
|
|
||||||
value: lightMode === 'light' ? 'dark' : 'light',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!serverSideApiKeyIsSet ? (
|
{!serverSideApiKeyIsSet ? (
|
||||||
|
@ -66,6 +67,13 @@ export const ChatbarSettings = () => {
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{!serverSidePluginKeysSet ? <PluginKeys /> : null}
|
{!serverSidePluginKeysSet ? <PluginKeys /> : null}
|
||||||
|
|
||||||
|
<SettingDialog
|
||||||
|
open={isSettingDialogOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsSettingDialog(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { FC, useContext, useEffect, useReducer, useRef } from 'react';
|
||||||
|
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
|
import { useCreateReducer } from '@/hooks/useCreateReducer';
|
||||||
|
|
||||||
|
import { getSettings, saveSettings } from '@/utils/app/settings';
|
||||||
|
|
||||||
|
import { Settings } from '@/types/settings';
|
||||||
|
|
||||||
|
import HomeContext from '@/pages/api/home/home.context';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SettingDialog: FC<Props> = ({ open, onClose }) => {
|
||||||
|
const { t } = useTranslation('settings');
|
||||||
|
const settings: Settings = getSettings();
|
||||||
|
const { state, dispatch } = useCreateReducer<Settings>({
|
||||||
|
initialState: settings,
|
||||||
|
});
|
||||||
|
const { dispatch: homeDispatch } = useContext(HomeContext);
|
||||||
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
||||||
|
window.addEventListener('mouseup', handleMouseUp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = (e: MouseEvent) => {
|
||||||
|
window.removeEventListener('mouseup', handleMouseUp);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('mousedown', handleMouseDown);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('mousedown', handleMouseDown);
|
||||||
|
};
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
homeDispatch({ field: 'lightMode', value: state.theme });
|
||||||
|
saveSettings(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render nothing if the dialog is not open.
|
||||||
|
if (!open) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the dialog.
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||||
|
<div className="fixed inset-0 z-10 overflow-hidden">
|
||||||
|
<div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||||
|
<div
|
||||||
|
className="hidden sm:inline-block sm:h-screen sm:align-middle"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref={modalRef}
|
||||||
|
className="dark:border-netural-400 inline-block max-h-[400px] transform overflow-y-auto rounded-lg border border-gray-300 bg-white px-4 pt-5 pb-4 text-left align-bottom shadow-xl transition-all dark:bg-[#202123] sm:my-8 sm:max-h-[600px] sm:w-full sm:max-w-lg sm:p-6 sm:align-middle"
|
||||||
|
role="dialog"
|
||||||
|
>
|
||||||
|
<div className="text-lg pb-4 font-bold text-black dark:text-neutral-200">
|
||||||
|
{t('Settings')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm font-bold mb-2 text-black dark:text-neutral-200">
|
||||||
|
{t('Theme')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<select
|
||||||
|
className="w-full cursor-pointer bg-transparent p-2 text-neutral-700 dark:text-neutral-200"
|
||||||
|
value={state.theme}
|
||||||
|
onChange={(event) =>
|
||||||
|
dispatch({ field: 'theme', value: event.target.value })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="dark">{t('Dark mode')}</option>
|
||||||
|
<option value="light">{t('Light mode')}</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-full px-4 py-2 mt-6 border rounded-lg shadow border-neutral-500 text-neutral-900 hover:bg-neutral-100 focus:outline-none dark:border-neutral-800 dark:border-opacity-50 dark:bg-white dark:text-black dark:hover:bg-neutral-300"
|
||||||
|
onClick={() => {
|
||||||
|
handleSave();
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Save')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -21,6 +21,7 @@ module.exports = {
|
||||||
"vi",
|
"vi",
|
||||||
"zh",
|
"zh",
|
||||||
"ar",
|
"ar",
|
||||||
|
"tr",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
localePath:
|
localePath:
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
} from '@/utils/app/conversation';
|
} from '@/utils/app/conversation';
|
||||||
import { saveFolders } from '@/utils/app/folders';
|
import { saveFolders } from '@/utils/app/folders';
|
||||||
import { savePrompts } from '@/utils/app/prompts';
|
import { savePrompts } from '@/utils/app/prompts';
|
||||||
|
import { getSettings } from '@/utils/app/settings';
|
||||||
|
|
||||||
import { Conversation } from '@/types/chat';
|
import { Conversation } from '@/types/chat';
|
||||||
import { KeyValuePair } from '@/types/data';
|
import { KeyValuePair } from '@/types/data';
|
||||||
|
@ -68,7 +69,7 @@ const Home = ({
|
||||||
conversations,
|
conversations,
|
||||||
selectedConversation,
|
selectedConversation,
|
||||||
prompts,
|
prompts,
|
||||||
temperature
|
temperature,
|
||||||
},
|
},
|
||||||
dispatch,
|
dispatch,
|
||||||
} = contextValue;
|
} = contextValue;
|
||||||
|
@ -191,7 +192,7 @@ const Home = ({
|
||||||
tokenLimit: OpenAIModels[defaultModelId].tokenLimit,
|
tokenLimit: OpenAIModels[defaultModelId].tokenLimit,
|
||||||
},
|
},
|
||||||
prompt: DEFAULT_SYSTEM_PROMPT,
|
prompt: DEFAULT_SYSTEM_PROMPT,
|
||||||
temperature: DEFAULT_TEMPERATURE,
|
temperature: lastConversation?.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
folderId: null,
|
folderId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -250,9 +251,12 @@ const Home = ({
|
||||||
// ON LOAD --------------------------------------------
|
// ON LOAD --------------------------------------------
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const theme = localStorage.getItem('theme');
|
const settings = getSettings();
|
||||||
if (theme) {
|
if (settings.theme) {
|
||||||
dispatch({ field: 'lightMode', value: theme as 'dark' | 'light' });
|
dispatch({
|
||||||
|
field: 'lightMode',
|
||||||
|
value: settings.theme,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiKey = localStorage.getItem('apiKey');
|
const apiKey = localStorage.getItem('apiKey');
|
||||||
|
@ -322,6 +326,7 @@ const Home = ({
|
||||||
value: cleanedSelectedConversation,
|
value: cleanedSelectedConversation,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
const lastConversation = conversations[conversations.length - 1];
|
||||||
dispatch({
|
dispatch({
|
||||||
field: 'selectedConversation',
|
field: 'selectedConversation',
|
||||||
value: {
|
value: {
|
||||||
|
@ -330,7 +335,7 @@ const Home = ({
|
||||||
messages: [],
|
messages: [],
|
||||||
model: OpenAIModels[defaultModelId],
|
model: OpenAIModels[defaultModelId],
|
||||||
prompt: DEFAULT_SYSTEM_PROMPT,
|
prompt: DEFAULT_SYSTEM_PROMPT,
|
||||||
temperature: DEFAULT_TEMPERATURE,
|
temperature: lastConversation?.temperature ?? DEFAULT_TEMPERATURE,
|
||||||
folderId: null,
|
folderId: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -419,6 +424,7 @@ export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
|
||||||
'sidebar',
|
'sidebar',
|
||||||
'markdown',
|
'markdown',
|
||||||
'promptbar',
|
'promptbar',
|
||||||
|
'settings',
|
||||||
])),
|
])),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "الوضع الداكن",
|
||||||
|
"Light mode": "الوضع الفاتح"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "ডার্ক মোড",
|
||||||
|
"Light mode": "লাইট মোড"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "আলাপচারিতা ইমপোর্ট",
|
"Import data": "আলাপচারিতা ইমপোর্ট",
|
||||||
"Are you sure?": "আপনি কি নিশ্চিত?",
|
"Are you sure?": "আপনি কি নিশ্চিত?",
|
||||||
"Clear conversations": "কথোপকথন পরিষ্কার করুন",
|
"Clear conversations": "কথোপকথন পরিষ্কার করুন",
|
||||||
"Export data": "আলাপচারিতা এক্সপোর্ট",
|
"Export data": "আলাপচারিতা এক্সপোর্ট"
|
||||||
"Dark mode": "ডার্ক মোড",
|
|
||||||
"Light mode": "লাইট মোড"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Dark Mode",
|
||||||
|
"Light mode": "Light Mode"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Konversationen importieren",
|
"Import data": "Konversationen importieren",
|
||||||
"Are you sure?": "Bist du sicher?",
|
"Are you sure?": "Bist du sicher?",
|
||||||
"Clear conversations": "Konversationen löschen",
|
"Clear conversations": "Konversationen löschen",
|
||||||
"Export data": "Konversationen exportieren",
|
"Export data": "Konversationen exportieren"
|
||||||
"Dark mode": "Dark Mode",
|
|
||||||
"Light mode": "Light Mode"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Modo oscuro",
|
||||||
|
"Light mode": "Modo claro"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Importar conversaciones",
|
"Import data": "Importar conversaciones",
|
||||||
"Are you sure?": "¿Estás seguro?",
|
"Are you sure?": "¿Estás seguro?",
|
||||||
"Clear conversations": "Borrar conversaciones",
|
"Clear conversations": "Borrar conversaciones",
|
||||||
"Export data": "Exportar conversaciones",
|
"Export data": "Exportar conversaciones"
|
||||||
"Dark mode": "Modo oscuro",
|
|
||||||
"Light mode": "Modo claro"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Mode sombre",
|
||||||
|
"Light mode": "Mode clair"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Importer des conversations",
|
"Import data": "Importer des conversations",
|
||||||
"Are you sure?": "Êtes-vous sûr ?",
|
"Are you sure?": "Êtes-vous sûr ?",
|
||||||
"Clear conversations": "Effacer les conversations",
|
"Clear conversations": "Effacer les conversations",
|
||||||
"Export data": "Exporter les conversations",
|
"Export data": "Exporter les conversations"
|
||||||
"Dark mode": "Mode sombre",
|
|
||||||
"Light mode": "Mode clair"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "מצב כהה",
|
||||||
|
"Light mode": "מצב בהיר"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "ייבוא שיחות",
|
"Import data": "ייבוא שיחות",
|
||||||
"Are you sure?": "אתה בטוח?",
|
"Are you sure?": "אתה בטוח?",
|
||||||
"Clear conversations": "ניקוי שיחות",
|
"Clear conversations": "ניקוי שיחות",
|
||||||
"Export data": "ייצוא שיחות",
|
"Export data": "ייצוא שיחות"
|
||||||
"Dark mode": "מצב כהה",
|
|
||||||
"Light mode": "מצב בהיר"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Mode gelap",
|
||||||
|
"Light mode": "Mode terang"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Impor percakapan",
|
"Import data": "Impor percakapan",
|
||||||
"Are you sure?": "Apakah Anda yakin?",
|
"Are you sure?": "Apakah Anda yakin?",
|
||||||
"Clear conversations": "Hapus percakapan",
|
"Clear conversations": "Hapus percakapan",
|
||||||
"Export data": "Ekspor percakapan",
|
"Export data": "Ekspor percakapan"
|
||||||
"Dark mode": "Mode gelap",
|
|
||||||
"Light mode": "Mode terang"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Modalità scura",
|
||||||
|
"Light mode": "Modalità chiara"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Importa dati",
|
"Import data": "Importa dati",
|
||||||
"Are you sure?": "Sei sicuro?",
|
"Are you sure?": "Sei sicuro?",
|
||||||
"Clear conversations": "Elimina conversazioni",
|
"Clear conversations": "Elimina conversazioni",
|
||||||
"Export data": "Esporta dati",
|
"Export data": "Esporta dati"
|
||||||
"Dark mode": "Modalità scura",
|
|
||||||
"Light mode": "Modalità chiara"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Settings": "設定",
|
||||||
|
"Theme": "テーマ",
|
||||||
|
"Save": "保存",
|
||||||
|
"Dark mode": "ダークモード",
|
||||||
|
"Light mode": "ライトモード"
|
||||||
|
}
|
|
@ -9,5 +9,6 @@
|
||||||
"Clear conversations": " 会話をクリア",
|
"Clear conversations": " 会話をクリア",
|
||||||
"Export data": "会話履歴をエクスポート",
|
"Export data": "会話履歴をエクスポート",
|
||||||
"Dark mode": "ダークモード",
|
"Dark mode": "ダークモード",
|
||||||
"Light mode": "ライトモード"
|
"Light mode": "ライトモード",
|
||||||
|
"Settings": "設定"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "다크 모드",
|
||||||
|
"Light mode": "라이트 모드"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "대화 가져오기",
|
"Import data": "대화 가져오기",
|
||||||
"Are you sure?": "확실합니까?",
|
"Are you sure?": "확실합니까?",
|
||||||
"Clear conversations": "대화 지우기",
|
"Clear conversations": "대화 지우기",
|
||||||
"Export data": "대화 내보내기",
|
"Export data": "대화 내보내기"
|
||||||
"Dark mode": "다크 모드",
|
|
||||||
"Light mode": "라이트 모드"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Tryb ciemny",
|
||||||
|
"Light mode": "Tryb jasny"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Import rozmów",
|
"Import data": "Import rozmów",
|
||||||
"Are you sure?": "Czy jesteś pewien?",
|
"Are you sure?": "Czy jesteś pewien?",
|
||||||
"Clear conversations": "Usuń rozmowy",
|
"Clear conversations": "Usuń rozmowy",
|
||||||
"Export data": "Eksport rozmów",
|
"Export data": "Eksport rozmów"
|
||||||
"Dark mode": "Tryb ciemny",
|
|
||||||
"Light mode": "Tryb jasny"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Modo escuro",
|
||||||
|
"Light mode": "Modo claro"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Importar conversas",
|
"Import data": "Importar conversas",
|
||||||
"Are you sure?": "Tem certeza?",
|
"Are you sure?": "Tem certeza?",
|
||||||
"Clear conversations": "Apagar conversas",
|
"Clear conversations": "Apagar conversas",
|
||||||
"Export data": "Exportar conversas",
|
"Export data": "Exportar conversas"
|
||||||
"Dark mode": "Modo escuro",
|
|
||||||
"Light mode": "Modo claro"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Modul întunecat",
|
||||||
|
"Light mode": "Modul de golire"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Importați conversații",
|
"Import data": "Importați conversații",
|
||||||
"Are you sure?": "Esti sigur?",
|
"Are you sure?": "Esti sigur?",
|
||||||
"Clear conversations": "Ștergeți conversațiile",
|
"Clear conversations": "Ștergeți conversațiile",
|
||||||
"Export data": "Exportați conversații",
|
"Export data": "Exportați conversații"
|
||||||
"Dark mode": "Modul întunecat",
|
}
|
||||||
"Light mode": "Modul de golire"
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Темный режим",
|
||||||
|
"Light mode": "Светлый режим"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Импортировать чаты",
|
"Import data": "Импортировать чаты",
|
||||||
"Are you sure?": "Вы уверены?",
|
"Are you sure?": "Вы уверены?",
|
||||||
"Clear conversations": "Удалить чаты",
|
"Clear conversations": "Удалить чаты",
|
||||||
"Export data": "Экспортировать чаты",
|
"Export data": "Экспортировать чаты"
|
||||||
"Dark mode": "Темный режим",
|
|
||||||
"Light mode": "Светлый режим"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "අඳුරු මාදිලිය",
|
||||||
|
"Light mode": "ආලෝක මාදිලිය"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "සංවාද ආයාත කරන්න",
|
"Import data": "සංවාද ආයාත කරන්න",
|
||||||
"Are you sure?": "ඔබට විශ්වාස ද?",
|
"Are you sure?": "ඔබට විශ්වාස ද?",
|
||||||
"Clear conversations": "සංවාද මකන්න",
|
"Clear conversations": "සංවාද මකන්න",
|
||||||
"Export data": "සංවාද නිර්යාත කරන්න",
|
"Export data": "සංවාද නිර්යාත කරන්න"
|
||||||
"Dark mode": "අඳුරු මාදිලිය",
|
|
||||||
"Light mode": "ආලෝක මාදිලිය"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Mörkt läge",
|
||||||
|
"Light mode": "Ljust läge"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Importera konversationer",
|
"Import data": "Importera konversationer",
|
||||||
"Are you sure?": "Är du säker?",
|
"Are you sure?": "Är du säker?",
|
||||||
"Clear conversations": "Radera konversationer",
|
"Clear conversations": "Radera konversationer",
|
||||||
"Export data": "Exportera konversationer",
|
"Export data": "Exportera konversationer"
|
||||||
"Dark mode": "Mörkt läge",
|
|
||||||
"Light mode": "Ljust läge"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "డార్క్ మోడ్",
|
||||||
|
"Light mode": "లైట్ మోడ్"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "సంభాషణలు దిగుమతి చేయండి",
|
"Import data": "సంభాషణలు దిగుమతి చేయండి",
|
||||||
"Are you sure?": "మీరు ఖచ్చితంగా ఉన్నారా?",
|
"Are you sure?": "మీరు ఖచ్చితంగా ఉన్నారా?",
|
||||||
"Clear conversations": "సంభాషణలు తొలగించు",
|
"Clear conversations": "సంభాషణలు తొలగించు",
|
||||||
"Export data": "సంభాషణలు ఎగుమతి చేయండి",
|
"Export data": "సంభాషణలు ఎగుమతి చేయండి"
|
||||||
"Dark mode": "డార్క్ మోడ్",
|
|
||||||
"Light mode": "లైట్ మోడ్"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"OpenAI API Key Required": "OpenAI API Anahtarı Gerekli",
|
||||||
|
"Please set your OpenAI API key in the bottom left of the sidebar.": "Lütfen OpenAI API anahtarınızı yan çubuğun sol altınan'dan ayarlayın.",
|
||||||
|
"Stop Generating": "Durdur",
|
||||||
|
"Prompt limit is {{maxLength}} characters": "Prompt sınırı {{maxLength}} karakterdir",
|
||||||
|
"System Prompt": "Sistem Prompt",
|
||||||
|
"You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully. Respond using markdown.": "Sen ChatGPT'sin, OpenAI tarafından eğitilmiş büyük bir dil modelisin. Kullanıcının talimatlarını dikkatlice takip et. Yanıtını markdown kullanarak ver.",
|
||||||
|
"Enter a prompt": "Bir prompt girin",
|
||||||
|
"Regenerate response": "Yanıtı Yeniden Oluştur",
|
||||||
|
"Sorry, there was an error.": "Üzgünüm, bir hata oluştu.",
|
||||||
|
"Model": "Model",
|
||||||
|
"Conversation": "Sohbet",
|
||||||
|
"OR": "VEYA",
|
||||||
|
"Loading...": "Yükleniyor...",
|
||||||
|
"Type a message...": "Bir mesaj yazın...",
|
||||||
|
"Error fetching models.": "Modeller getirilirken hata oluştu.",
|
||||||
|
"AI": "Yapay Zeka",
|
||||||
|
"You": "Sen",
|
||||||
|
"Cancel": "İptal",
|
||||||
|
"Save & Submit": "Kaydet ve Gönder",
|
||||||
|
"Make sure your OpenAI API key is set in the bottom left of the sidebar.": "OpenAI API anahtarınızın yan çubuğun sol altında ayarlandığından emin ol.",
|
||||||
|
"If you completed this step, OpenAI may be experiencing issues.": "Bu adımı tamamladıysanız, OpenAI sorun yaşıyor olabilir.",
|
||||||
|
"click if using a .env.local file": "Eğer .env.local dosyası kullanıyorsanız tıklayın",
|
||||||
|
"Message limit is {{maxLength}} characters. You have entered {{valueLength}} characters.": "Mesaj sınırı {{maxLength}} karakterdir. {{valueLength}} karakter girdiniz.",
|
||||||
|
"Please enter a message": "Lütfen bir mesaj girin",
|
||||||
|
"Chatbot UI is an advanced chatbot kit for OpenAI's chat models aiming to mimic ChatGPT's interface and functionality.": "Chatbot UI, ChatGPT'nin arayüz ve işlevselliğini taklit etmeyi amaçlayan OpenAI'in sohbet modelleri için gelişmiş bir sohbetbot kitidir.",
|
||||||
|
"Are you sure you want to clear all messages?": "Tüm mesajları temizlemek istediğinize emin misiniz?"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"Copy code": "Kodu kopyala",
|
||||||
|
"Copied!": "Kopyalandi!",
|
||||||
|
"Enter file name": "Dosya ismi gir"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"New prompt": "Yeni prompt",
|
||||||
|
"New folder": "Yeni klasör",
|
||||||
|
"No prompts.": "Prompt yok.",
|
||||||
|
"Search prompts...": "Prompt'ları ara...",
|
||||||
|
"Name": "İsim",
|
||||||
|
"Description": "Açıklama",
|
||||||
|
"A description for your prompt.": "Prompt'unuz için bir açıklama.",
|
||||||
|
"Prompt": "Prompt",
|
||||||
|
"Prompt content. Use {{}} to denote a variable. Ex: {{name}} is a {{adjective}} {{noun}}": "Prompt içeriği. Değişken belirtmek için {{}} kullanın. Örn: {{ad}} bir {{sıfat}} {{isim}}",
|
||||||
|
"Save": "Kaydet"
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"New folder": "Yeni klasör",
|
||||||
|
"New chat": "Yeni sohbet",
|
||||||
|
"No conversations.": "Hiçbir konuşma yok.",
|
||||||
|
"Search conversations...": "Sohbetleri ara...",
|
||||||
|
"OpenAI API Key": "OpenAI API Anahtarı",
|
||||||
|
"Import data": "Veri içe aktar",
|
||||||
|
"Are you sure?": "Emin misiniz?",
|
||||||
|
"Clear conversations": "Konuşmaları temizle",
|
||||||
|
"Export data": "Veri dışa aktar",
|
||||||
|
"Dark mode": "Karanlık mod",
|
||||||
|
"Light mode": "Aydınlık mod",
|
||||||
|
"Plugin Keys": "Plugin Anahtarları"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "Chế độ tối",
|
||||||
|
"Light mode": "Chế độ sáng"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "Nhập dữ liệu hội thoại",
|
"Import data": "Nhập dữ liệu hội thoại",
|
||||||
"Are you sure?": "Bạn chắc chắn chứ?",
|
"Are you sure?": "Bạn chắc chắn chứ?",
|
||||||
"Clear conversations": "Xoá các đoạn hội thoại",
|
"Clear conversations": "Xoá các đoạn hội thoại",
|
||||||
"Export data": "Xuất dữ liệu hội thoại",
|
"Export data": "Xuất dữ liệu hội thoại"
|
||||||
"Dark mode": "Chế độ tối",
|
|
||||||
"Light mode": "Chế độ sáng"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"Dark mode": "深色模式",
|
||||||
|
"Light mode": "浅色模式"
|
||||||
|
}
|
|
@ -7,7 +7,5 @@
|
||||||
"Import data": "导入对话",
|
"Import data": "导入对话",
|
||||||
"Are you sure?": "确定吗?",
|
"Are you sure?": "确定吗?",
|
||||||
"Clear conversations": "清空对话",
|
"Clear conversations": "清空对话",
|
||||||
"Export data": "导出对话",
|
"Export data": "导出对话"
|
||||||
"Dark mode": "深色模式",
|
|
||||||
"Light mode": "浅色模式"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface Settings {
|
||||||
|
theme: 'light' | 'dark';
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ export const cleanSelectedConversation = (conversation: Conversation) => {
|
||||||
// added system prompt for each conversation (3/21/23)
|
// added system prompt for each conversation (3/21/23)
|
||||||
// added folders (3/23/23)
|
// added folders (3/23/23)
|
||||||
// added prompts (3/26/23)
|
// added prompts (3/26/23)
|
||||||
|
// added messages (4/16/23)
|
||||||
|
|
||||||
let updatedConversation = conversation;
|
let updatedConversation = conversation;
|
||||||
|
|
||||||
|
@ -41,6 +42,13 @@ export const cleanSelectedConversation = (conversation: Conversation) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!updatedConversation.messages) {
|
||||||
|
updatedConversation = {
|
||||||
|
...updatedConversation,
|
||||||
|
messages: updatedConversation.messages || [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return updatedConversation;
|
return updatedConversation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,6 +57,7 @@ export const cleanConversationHistory = (history: any[]): Conversation[] => {
|
||||||
// added system prompt for each conversation (3/21/23)
|
// added system prompt for each conversation (3/21/23)
|
||||||
// added folders (3/23/23)
|
// added folders (3/23/23)
|
||||||
// added prompts (3/26/23)
|
// added prompts (3/26/23)
|
||||||
|
// added messages (4/16/23)
|
||||||
|
|
||||||
if (!Array.isArray(history)) {
|
if (!Array.isArray(history)) {
|
||||||
console.warn('history is not an array. Returning an empty array.');
|
console.warn('history is not an array. Returning an empty array.');
|
||||||
|
@ -73,6 +82,10 @@ export const cleanConversationHistory = (history: any[]): Conversation[] => {
|
||||||
conversation.folderId = null;
|
conversation.folderId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!conversation.messages) {
|
||||||
|
conversation.messages = [];
|
||||||
|
}
|
||||||
|
|
||||||
acc.push(conversation);
|
acc.push(conversation);
|
||||||
return acc;
|
return acc;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { Settings } from '@/types/settings';
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'settings';
|
||||||
|
|
||||||
|
export const getSettings = (): Settings => {
|
||||||
|
let settings: Settings = {
|
||||||
|
theme: 'dark',
|
||||||
|
};
|
||||||
|
const settingsJson = localStorage.getItem(STORAGE_KEY);
|
||||||
|
if (settingsJson) {
|
||||||
|
try {
|
||||||
|
let savedSettings = JSON.parse(settingsJson) as Settings;
|
||||||
|
settings = Object.assign(settings, savedSettings);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveSettings = (settings: Settings) => {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
||||||
|
};
|
Loading…
Reference in New Issue