mirror of
https://github.com/NovaOSS/nova-betterchat.git
synced 2024-11-25 17:24:00 +01:00
api popup
This commit is contained in:
parent
58858d51ac
commit
aefca02620
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "Brug gratis",
|
||||
"inputLabel": "API Endpoint",
|
||||
"description": "Tak til <0>Ayaka</0> for at levere det gratis API-endpoint: https://chatgpt-api.shn.hk/v1/"
|
||||
"description": "Tak til <0>Ayaka</0> for at levere det gratis API-endpoint: https://chatgpt-api.shn.hk/v1/",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "Brug din egen API-nøgle",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "API-nøgle"
|
||||
},
|
||||
"customEndpoint": "Brug brugerdefineret API-endpoint",
|
||||
"shareGPT": "Tjek <0>ShareGPT</0>, et API-nøgle delingsværktøj, der driver https://sharegpt.churchless.tech/share/v1/chat"
|
||||
"shareGPT": "Tjek <0>ShareGPT</0>, et API-nøgle delingsværktøj, der driver https://sharegpt.churchless.tech/share/v1/chat",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "Use for free",
|
||||
"inputLabel": "API Endpoint",
|
||||
"description": "Thank you to <0>Ayaka</0> for providing the free API endpoint: https://chatgpt-api.shn.hk/v1/"
|
||||
"description": "Thank you to <0>Ayaka</0> for providing the free API endpoint: https://chatgpt-api.shn.hk/v1/",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "Use your own API key",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "API Key"
|
||||
},
|
||||
"customEndpoint": "Use custom API endpoint",
|
||||
"shareGPT": "Checkout <0>ShareGPT</0>, a API key sharing tool that powers https://sharegpt.churchless.tech/share/v1/chat"
|
||||
"shareGPT": "Checkout <0>ShareGPT</0>, a API key sharing tool that powers https://sharegpt.churchless.tech/share/v1/chat",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "Uso gratuito",
|
||||
"inputLabel": "Punto final de acceso de la API",
|
||||
"description": "Gracias a <0>Ayaka</0> por proveer el punto final de acceso gratuito: https://chatgpt-api.shn.hk/v1/"
|
||||
"description": "Gracias a <0>Ayaka</0> por proveer el punto final de acceso gratuito: https://chatgpt-api.shn.hk/v1/",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "Usa tu propia clave API",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "Clave API"
|
||||
},
|
||||
"customEndpoint": "Usar un punto final de acceso personalizado",
|
||||
"shareGPT": "Echa un vistazo a <0>ShareGPT</0>, una herramienta que permite compartir claves API: https://sharegpt.churchless.tech/share/v1/chat"
|
||||
"shareGPT": "Echa un vistazo a <0>ShareGPT</0>, una herramienta que permite compartir claves API: https://sharegpt.churchless.tech/share/v1/chat",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "Bruk gratis",
|
||||
"inputLabel": "API-sluttpunkt",
|
||||
"description": "Takk til <0>Ayaka</0> for å tilby det gratis API-sluttpunktet: https://chatgpt-api.shn.hk/v1/"
|
||||
"description": "Takk til <0>Ayaka</0> for å tilby det gratis API-sluttpunktet: https://chatgpt-api.shn.hk/v1/",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "Bruk din egen API-nøkkel",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "API-nøkkel"
|
||||
},
|
||||
"customEndpoint": "Bruk egendefinert API-sluttpunkt",
|
||||
"shareGPT": "Sjekk ut <0>ShareGPT</0>, et API-nøkkel delingsverktøy som driver https://sharegpt.churchless.tech/share/v1/chat"
|
||||
"shareGPT": "Sjekk ut <0>ShareGPT</0>, et API-nøkkel delingsverktøy som driver https://sharegpt.churchless.tech/share/v1/chat",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "Använd gratis",
|
||||
"inputLabel": "API Endpoint",
|
||||
"description": "Tack till <0>Ayaka</0> för att ha tillhandahållit den kostnadsfria API-endpointen: https://chatgpt-api.shn.hk/v1/"
|
||||
"description": "Tack till <0>Ayaka</0> för att ha tillhandahållit den kostnadsfria API-endpointen: https://chatgpt-api.shn.hk/v1/",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "Använd din egen API-nyckel",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "API-nyckel"
|
||||
},
|
||||
"customEndpoint": "Använd anpassad API-endpoint",
|
||||
"shareGPT": "Kolla in <0>ShareGPT</0>, ett API-nyckeldelningsverktyg som möjliggör https://sharegpt.churchless.tech/share/v1/chat"
|
||||
"shareGPT": "Kolla in <0>ShareGPT</0>, ett API-nyckeldelningsverktyg som möjliggör https://sharegpt.churchless.tech/share/v1/chat",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "使用免费的 API 端点",
|
||||
"inputLabel": "API 端点",
|
||||
"description": "感谢 <0>Ayaka</0> 提供免费的 API 端点: https://chatgpt-api.shn.hk/v1/。"
|
||||
"description": "感谢 <0>Ayaka</0> 提供免费的 API 端点: https://chatgpt-api.shn.hk/v1/。",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "使用自己的 API 密钥",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "API 密钥"
|
||||
},
|
||||
"customEndpoint": "使用自定义 API 端点",
|
||||
"shareGPT": "看看 <0>ShareGPT</0>,一个 API key 共享工具,驱动 https://sharegpt.churchless.tech/share/v1/chat。"
|
||||
"shareGPT": "看看 <0>ShareGPT</0>,一个 API key 共享工具,驱动 https://sharegpt.churchless.tech/share/v1/chat。",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "使用免費嘅 API 端點",
|
||||
"inputLabel": "API 端點",
|
||||
"description": "感謝 <0>Ayaka</0> 提供免費嘅 API 端點: https://chatgpt-api.shn.hk/v1/。"
|
||||
"description": "感謝 <0>Ayaka</0> 提供免費嘅 API 端點: https://chatgpt-api.shn.hk/v1/。",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "使用自己嘅 API 金鑰",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "API 金鑰"
|
||||
},
|
||||
"customEndpoint": "使用自訂 API 端點",
|
||||
"shareGPT": "睇下 ShareGPT,一個 API key 共享工具,驅動 https://sharegpt.churchless.tech/share/v1/chat。"
|
||||
"shareGPT": "睇下 ShareGPT,一個 API key 共享工具,驅動 https://sharegpt.churchless.tech/share/v1/chat。",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"apiEndpoint": {
|
||||
"option": "使用免費的 API 端點",
|
||||
"inputLabel": "API 端點",
|
||||
"description": "感謝 <0>Ayaka</0> 提供免費的 API 端點: https://chatgpt-api.shn.hk/v1/。"
|
||||
"description": "感謝 <0>Ayaka</0> 提供免費的 API 端點: https://chatgpt-api.shn.hk/v1/。",
|
||||
"note": "Please note that the availability of the free endpoint is not indefinite, as its funding is limited and will eventually be depleted. Users with their own API key can continue using BetterChatGPT without any disruptions."
|
||||
},
|
||||
"apiKey": {
|
||||
"option": "使用自己的 API 金鑰",
|
||||
|
@ -11,5 +12,7 @@
|
|||
"inputLabel": "API 金鑰"
|
||||
},
|
||||
"customEndpoint": "使用自定義 API 端點",
|
||||
"shareGPT": "看看 <0>ShareGPT</0>,一個 API key 共享工具,驅動 https://sharegpt.churchless.tech/share/v1/chat。"
|
||||
"shareGPT": "看看 <0>ShareGPT</0>,一個 API key 共享工具,驅動 https://sharegpt.churchless.tech/share/v1/chat。",
|
||||
"advancedConfig": "View advanced API configuration <0>here</0>",
|
||||
"noApiKeyWarning": "No API key supplied! Please check your API settings."
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import Menu from '@components/Menu';
|
|||
import useInitialiseNewChat from '@hooks/useInitialiseNewChat';
|
||||
import { ChatInterface } from '@type/chat';
|
||||
import { Theme } from '@type/theme';
|
||||
import ApiPopup from '@components/ApiPopup';
|
||||
|
||||
function App() {
|
||||
const initialiseNewChat = useInitialiseNewChat();
|
||||
|
@ -68,6 +69,7 @@ function App() {
|
|||
<div className='overflow-hidden w-full h-full relative'>
|
||||
<Menu />
|
||||
<Chat />
|
||||
<ApiPopup />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -89,6 +89,12 @@ const ApiMenu = ({
|
|||
{t('apiEndpoint.option', { ns: 'api' })}
|
||||
</label>
|
||||
|
||||
{_apiFree && (
|
||||
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm my-2'>
|
||||
{t('apiEndpoint.note', { ns: 'api' })}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<label className='flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-gray-300'>
|
||||
<input
|
||||
type='radio'
|
||||
|
|
113
src/components/ApiPopup/ApiPopup.tsx
Normal file
113
src/components/ApiPopup/ApiPopup.tsx
Normal file
|
@ -0,0 +1,113 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import useStore from '@store/store';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
|
||||
import PopupModal from '@components/PopupModal';
|
||||
import CrossIcon from '@icon/CrossIcon';
|
||||
|
||||
const ApiPopup = () => {
|
||||
const { t } = useTranslation(['main', 'api']);
|
||||
|
||||
const apiKey = useStore((state) => state.apiKey);
|
||||
const setApiKey = useStore((state) => state.setApiKey);
|
||||
const setApiFree = useStore((state) => state.setApiFree);
|
||||
const firstVisit = useStore((state) => state.firstVisit);
|
||||
const setFirstVisit = useStore((state) => state.setFirstVisit);
|
||||
|
||||
const [_apiKey, _setApiKey] = useState<string>(apiKey || '');
|
||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(firstVisit);
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (_apiKey.length === 0) {
|
||||
setError(t('noApiKeyWarning', { ns: 'api' }) as string);
|
||||
} else {
|
||||
setError('');
|
||||
setApiFree(false);
|
||||
setApiKey(_apiKey);
|
||||
setIsModalOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setFirstVisit(false);
|
||||
}, []);
|
||||
|
||||
return isModalOpen ? (
|
||||
<PopupModal
|
||||
title='Setup your API key'
|
||||
handleConfirm={handleConfirm}
|
||||
setIsModalOpen={setIsModalOpen}
|
||||
cancelButton={false}
|
||||
>
|
||||
<div className='p-6 border-b border-gray-200 dark:border-gray-600'>
|
||||
<div className='flex gap-2 items-center justify-center mt-2'>
|
||||
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm'>
|
||||
{t('apiKey.inputLabel', { ns: 'api' })}
|
||||
</div>
|
||||
<input
|
||||
type='text'
|
||||
className='text-gray-800 dark:text-white p-3 text-sm border-none bg-gray-200 dark:bg-gray-600 rounded-md m-0 w-full mr-0 h-8 focus:outline-none'
|
||||
value={_apiKey}
|
||||
onChange={(e) => {
|
||||
_setApiKey(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm mt-4'>
|
||||
<Trans
|
||||
i18nKey='apiKey.howTo'
|
||||
ns='api'
|
||||
components={[
|
||||
<a
|
||||
href='https://platform.openai.com/account/api-keys'
|
||||
className='link'
|
||||
target='_blank'
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm mt-4'>
|
||||
<Trans
|
||||
i18nKey='advancedConfig'
|
||||
ns='api'
|
||||
components={[
|
||||
<a
|
||||
className='link cursor-pointer'
|
||||
onClick={() => {
|
||||
setIsModalOpen(false);
|
||||
document.getElementById('api-menu')?.click();
|
||||
}}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='min-w-fit text-gray-900 dark:text-gray-300 text-sm mt-4'>
|
||||
{t('securityMessage', { ns: 'api' })}
|
||||
</div>
|
||||
|
||||
{error.length > 0 && (
|
||||
<div className='relative py-2 px-3 w-full mt-3 border rounded-md border-red-500 bg-red-500/10'>
|
||||
<div className='text-gray-600 dark:text-gray-100 text-sm whitespace-pre-wrap'>
|
||||
{error}
|
||||
</div>
|
||||
<div
|
||||
className='text-white absolute top-1 right-1 cursor-pointer'
|
||||
onClick={() => {
|
||||
setError('');
|
||||
}}
|
||||
>
|
||||
<CrossIcon />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</PopupModal>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApiPopup;
|
1
src/components/ApiPopup/index.ts
Normal file
1
src/components/ApiPopup/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './ApiPopup';
|
|
@ -14,6 +14,7 @@ const Config = () => {
|
|||
<>
|
||||
<a
|
||||
className='flex py-3 px-3 items-center gap-3 rounded-md hover:bg-gray-500/10 transition-colors duration-200 text-white cursor-pointer text-sm'
|
||||
id='api-menu'
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
<PersonIcon />
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
export const defaultAPIEndpoint =
|
||||
'https://sharegpt.churchless.tech/share/v1/chat';
|
||||
export const officialAPIEndpoint = 'https://api.openai.com/v1/chat/completions';
|
||||
export const defaultAPIEndpoint = officialAPIEndpoint;
|
||||
|
||||
export const availableEndpoints = [
|
||||
'https://sharegpt.churchless.tech/share/v1/chat',
|
||||
'https://chatgpt-api.shn.hk/v1/',
|
||||
officialAPIEndpoint,
|
||||
'https://chatgpt-api.shn.hk/v1/',
|
||||
'https://sharegpt.churchless.tech/share/v1/chat',
|
||||
];
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import useStore from '@store/store';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ChatInterface, MessageInterface } from '@type/chat';
|
||||
import { getChatCompletion, getChatCompletionStream } from '@api/api';
|
||||
import { parseEventSource } from '@api/helper';
|
||||
|
@ -7,6 +8,7 @@ import { limitMessageTokens } from '@utils/messageUtils';
|
|||
import { _defaultChatConfig } from '@constants/chat';
|
||||
|
||||
const useSubmit = () => {
|
||||
const { t } = useTranslation('api');
|
||||
const error = useStore((state) => state.error);
|
||||
const setError = useStore((state) => state.setError);
|
||||
const apiFree = useStore((state) => state.apiFree);
|
||||
|
@ -73,7 +75,7 @@ const useSubmit = () => {
|
|||
apiKey
|
||||
);
|
||||
} else {
|
||||
throw new Error('No API key supplied! Please check your API settings.');
|
||||
throw new Error(t('noApiKeyWarning') as string);
|
||||
}
|
||||
|
||||
if (stream) {
|
||||
|
|
|
@ -5,14 +5,17 @@ export interface AuthSlice {
|
|||
apiKey?: string;
|
||||
apiFree: boolean;
|
||||
apiEndpoint: string;
|
||||
firstVisit: boolean;
|
||||
setApiKey: (apiKey: string) => void;
|
||||
setApiFree: (apiFree: boolean) => void;
|
||||
setApiEndpoint: (apiEndpoint: string) => void;
|
||||
setFirstVisit: (firstVisit: boolean) => void;
|
||||
}
|
||||
|
||||
export const createAuthSlice: StoreSlice<AuthSlice> = (set, get) => ({
|
||||
apiFree: true,
|
||||
apiFree: false,
|
||||
apiEndpoint: defaultAPIEndpoint,
|
||||
firstVisit: true,
|
||||
setApiKey: (apiKey: string) => {
|
||||
set((prev: AuthSlice) => ({
|
||||
...prev,
|
||||
|
@ -31,4 +34,10 @@ export const createAuthSlice: StoreSlice<AuthSlice> = (set, get) => ({
|
|||
apiEndpoint: apiEndpoint,
|
||||
}));
|
||||
},
|
||||
setFirstVisit: (firstVisit: boolean) => {
|
||||
set((prev: AuthSlice) => ({
|
||||
...prev,
|
||||
firstVisit: firstVisit,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
|
|
@ -55,6 +55,7 @@ const useStore = create<StoreState>()(
|
|||
prompts: state.prompts,
|
||||
defaultChatConfig: state.defaultChatConfig,
|
||||
defaultSystemMessage: state.defaultSystemMessage,
|
||||
firstVisit: state.firstVisit,
|
||||
}),
|
||||
version: 6,
|
||||
migrate: (persistedState, version) => {
|
||||
|
|
Loading…
Reference in a new issue