chore: some small improvements (#223)
* chore: stylize error message div chore: correct styles for sidebar btn chore: add spinner and replace header "Please wait" on spinner chore: correct Russian translate chore: hide clear conversation btn if not conversations chore: stylize "Need OpenAI key" div * chore: corrent Russian translate
This commit is contained in:
parent
d8e3844fb9
commit
3ca503a3f2
|
@ -6,9 +6,10 @@ import {
|
|||
OpenAIModel,
|
||||
} from '@/types';
|
||||
import { throttle } from '@/utils';
|
||||
import { IconClearAll, IconSettings } from '@tabler/icons-react';
|
||||
import { IconClearAll, IconKey, IconSettings } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { FC, memo, MutableRefObject, useEffect, useRef, useState } from 'react';
|
||||
import { Spinner } from '../Global/Spinner';
|
||||
import { ChatInput } from './ChatInput';
|
||||
import { ChatLoader } from './ChatLoader';
|
||||
import { ChatMessage } from './ChatMessage';
|
||||
|
@ -110,24 +111,31 @@ export const Chat: FC<Props> = memo(
|
|||
<div className="overflow-none relative flex-1 bg-white dark:bg-[#343541]">
|
||||
{!(apiKey || serverSideApiKeyIsSet) ? (
|
||||
<div className="mx-auto flex h-full w-[300px] flex-col justify-center space-y-6 sm:w-[500px]">
|
||||
<div className="mx-auto mb-5">
|
||||
<IconKey size={36} />
|
||||
</div>
|
||||
<div className="text-center text-2xl font-semibold text-gray-800 dark:text-gray-100">
|
||||
{t('OpenAI API Key Required')}
|
||||
</div>
|
||||
<div className="text-center text-gray-500 dark:text-gray-400">
|
||||
{t(
|
||||
'Please set your OpenAI API key in the bottom left of the sidebar.',
|
||||
)}
|
||||
</div>
|
||||
<div className="text-center text-gray-500 dark:text-gray-400">
|
||||
{t("If you don't have an OpenAI API key, you can get one here: ")}
|
||||
<a
|
||||
href="https://platform.openai.com/account/api-keys"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
openai.com
|
||||
</a>
|
||||
<div className="mb-2">
|
||||
{t(
|
||||
'Please set your OpenAI API key in the bottom left of the sidebar.',
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{t(
|
||||
"If you don't have an OpenAI API key, you can get one here: ",
|
||||
)}
|
||||
<a
|
||||
href="https://platform.openai.com/account/api-keys"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
openai.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : modelError ? (
|
||||
|
@ -142,11 +150,17 @@ export const Chat: FC<Props> = memo(
|
|||
<>
|
||||
<div className="mx-auto flex w-[350px] flex-col space-y-10 pt-12 sm:w-[600px]">
|
||||
<div className="text-center text-3xl font-semibold text-gray-800 dark:text-gray-100">
|
||||
{models.length === 0 ? t('Loading...') : 'Chatbot UI'}
|
||||
{models.length === 0 ? (
|
||||
<div>
|
||||
<Spinner size="16px" className="mx-auto" />
|
||||
</div>
|
||||
) : (
|
||||
'Chatbot UI'
|
||||
)}
|
||||
</div>
|
||||
|
||||
{models.length > 0 && (
|
||||
<div className="flex h-full flex-col space-y-4 rounded border border-neutral-200 p-4 dark:border-neutral-600">
|
||||
<div className="flex h-full flex-col space-y-4 rounded-lg border border-neutral-200 p-4 dark:border-neutral-600">
|
||||
<ModelSelect
|
||||
model={conversation.model}
|
||||
models={models}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { ErrorMessage } from '@/types';
|
||||
import { IconCircleX } from '@tabler/icons-react';
|
||||
import { FC } from 'react';
|
||||
|
||||
interface Props {
|
||||
|
@ -7,16 +8,20 @@ interface Props {
|
|||
|
||||
export const ErrorMessageDiv: FC<Props> = ({ error }) => {
|
||||
return (
|
||||
<div className="mx-auto flex h-full w-[300px] flex-col justify-center space-y-6 sm:w-[500px]">
|
||||
<div className="text-center text-red-500">
|
||||
{error.title} {error.code ? <i>({error.code}) </i> : ''}
|
||||
<div className="mx-6 flex h-full flex-col items-center justify-center text-red-500">
|
||||
<div className="mb-5">
|
||||
<IconCircleX size={36} />
|
||||
</div>
|
||||
<div className="mb-3 text-2xl font-medium">{error.title}</div>
|
||||
{error.messageLines.map((line, index) => (
|
||||
<div key={index} className="text-center text-red-500">
|
||||
<div key={index} className="text-center">
|
||||
{' '}
|
||||
{line}{' '}
|
||||
</div>
|
||||
))}
|
||||
<div className="text-xs dark:text-red-400 opacity-50 mt-4">
|
||||
{error.code ? <i>Code: {error.code}</i> : ''}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
interface Props {
|
||||
size?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Spinner: FC<Props> = ({ size = '1em', className="" }) => {
|
||||
return (
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={`animate-spin ${className}`}
|
||||
height={size}
|
||||
width={size}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<line x1="12" y1="2" x2="12" y2="6"></line>
|
||||
<line x1="12" y1="18" x2="12" y2="22"></line>
|
||||
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
|
||||
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
|
||||
<line x1="2" y1="12" x2="6" y2="12"></line>
|
||||
<line x1="18" y1="12" x2="22" y2="12"></line>
|
||||
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
|
||||
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
|
||||
</svg>
|
||||
);
|
||||
};
|
|
@ -18,7 +18,7 @@ export const ClearConversations: FC<Props> = ({ onClearConversations }) => {
|
|||
};
|
||||
|
||||
return isConfirming ? (
|
||||
<div className="flex w-full cursor-pointer items-center rounded-md py-3 px-3 hover:bg-[#343541]">
|
||||
<div className="flex w-full cursor-pointer items-center rounded-lg py-3 px-3 hover:bg-gray-500/10">
|
||||
<IconTrash size={18} />
|
||||
|
||||
<div className="ml-3 flex-1 text-left text-[12.5px] leading-3 text-white">
|
||||
|
|
|
@ -30,7 +30,7 @@ export const Key: FC<Props> = ({ apiKey, onApiKeyChange }) => {
|
|||
<IconKey size={18} />
|
||||
|
||||
<input
|
||||
className="ml-2 h-[20px] flex-1 overflow-hidden overflow-ellipsis border-b border-neutral-400 bg-transparent pr-1 text-left text-white outline-none focus:border-neutral-100"
|
||||
className="ml-2 h-[20px] flex-1 overflow-hidden overflow-ellipsis border-b border-neutral-400 bg-transparent pr-1 text-[12.5px] leading-3 text-left text-white outline-none focus:border-neutral-100"
|
||||
type="password"
|
||||
value={newKey}
|
||||
onChange={(e) => setNewKey(e.target.value)}
|
||||
|
|
|
@ -202,6 +202,7 @@ export const Sidebar: FC<Props> = ({
|
|||
<SidebarSettings
|
||||
lightMode={lightMode}
|
||||
apiKey={apiKey}
|
||||
conversationsCount={conversations.length}
|
||||
onToggleLightMode={onToggleLightMode}
|
||||
onApiKeyChange={onApiKeyChange}
|
||||
onClearConversations={onClearConversations}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { SidebarButton } from './SidebarButton';
|
|||
interface Props {
|
||||
lightMode: 'light' | 'dark';
|
||||
apiKey: string;
|
||||
conversationsCount: number;
|
||||
onToggleLightMode: (mode: 'light' | 'dark') => void;
|
||||
onApiKeyChange: (apiKey: string) => void;
|
||||
onClearConversations: () => void;
|
||||
|
@ -23,6 +24,7 @@ interface Props {
|
|||
export const SidebarSettings: FC<Props> = ({
|
||||
lightMode,
|
||||
apiKey,
|
||||
conversationsCount,
|
||||
onToggleLightMode,
|
||||
onApiKeyChange,
|
||||
onClearConversations,
|
||||
|
@ -32,7 +34,9 @@ export const SidebarSettings: FC<Props> = ({
|
|||
const { t } = useTranslation('sidebar');
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-1 border-t border-white/20 pt-1 text-sm">
|
||||
<ClearConversations onClearConversations={onClearConversations} />
|
||||
{conversationsCount > 0 ? (
|
||||
<ClearConversations onClearConversations={onClearConversations} />
|
||||
) : null}
|
||||
|
||||
<Import onImport={onImportConversations} />
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"System Prompt": "Системное сообщение",
|
||||
"You are ChatGPT, a large language model trained by OpenAI. Follow the user's instructions carefully. Respond using markdown.": "Вы ChatGPT, большая языковая модель, созданная компанией OpenAI. Следуйте инструкциям пользователя. Отвечайте на сообщения, используя Markdown",
|
||||
"Enter a prompt": "Введите сообщение",
|
||||
"Regenerate response": "Сгенерировать сообщение снова",
|
||||
"Regenerate response": "Перегенерировать сообщение",
|
||||
"Sorry, there was an error.": "Просим прощения, произошла ошибка",
|
||||
"Model": "Модель",
|
||||
"Conversation": "Чат",
|
||||
|
@ -17,11 +17,11 @@
|
|||
"AI": "Бот",
|
||||
"You": "Вы",
|
||||
"Cancel": "Отмена",
|
||||
"Save & Submit": "Сохранить и отправить",
|
||||
"Save & Submit": "Отредактировать",
|
||||
"Make sure your OpenAI API key is set in the bottom left of the sidebar.": "Убедитесь, что вы ввели API-ключ OpenAI.",
|
||||
"If you completed this step, OpenAI may be experiencing issues.": "Если вы выполнили этот шаг, то возможно OpenAI может испытывать проблемы",
|
||||
"Message limit is {{maxLength}} characters. You have entered {{valueLength}} characters.": "Лимит сообщения: {{maxLength}} символов. Вы ввели {{valueLength}} символов.",
|
||||
"Please enter a message": "Пожалуйста введите сообщение",
|
||||
"Chatbot UI is an advanced chatbot kit for OpenAI's chat models aiming to mimic ChatGPT's interface and functionality.": "Chatbot UI - продвинутый интерфейс чатбота для чат-моделей OpenAI, которое имитирует интерфейс ChatGPT",
|
||||
"Chatbot UI is an advanced chatbot kit for OpenAI's chat models aiming to mimic ChatGPT's interface and functionality.": "Chatbot UI - продвинутый интерфейс чатбота для чат-моделей OpenAI, имитирующий интерфейс ChatGPT",
|
||||
"Are you sure you want to clear all messages?": "Вы уверены, что хотите удалить все сообщения?"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue