Prompts (#229)
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { Conversation } from '@/types/chat';
|
||||
import { Folder } from '@/types/folder';
|
||||
import { cleanConversationHistory } from '@/utils/app/clean';
|
||||
import { IconFileImport } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { FC } from 'react';
|
||||
import { SidebarButton } from '../Sidebar/SidebarButton';
|
||||
|
||||
interface Props {
|
||||
onImport: (data: {
|
||||
conversations: Conversation[];
|
||||
folders: Folder[];
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export const Import: FC<Props> = ({ onImport }) => {
|
||||
const { t } = useTranslation('sidebar');
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
id="import-file"
|
||||
className="sr-only"
|
||||
tabIndex={-1}
|
||||
type="file"
|
||||
accept=".json"
|
||||
onChange={(e) => {
|
||||
if (!e.target.files?.length) return;
|
||||
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
let json = JSON.parse(e.target?.result as string);
|
||||
|
||||
if (json && !json.folders) {
|
||||
json = { history: cleanConversationHistory(json), folders: [] };
|
||||
}
|
||||
|
||||
onImport({ conversations: json.history, folders: json.folders });
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}}
|
||||
/>
|
||||
|
||||
<SidebarButton
|
||||
text={t('Import conversations')}
|
||||
icon={<IconFileImport size={18} />}
|
||||
onClick={() => {
|
||||
const importFile = document.querySelector(
|
||||
'#import-file',
|
||||
) as HTMLInputElement;
|
||||
if (importFile) {
|
||||
importFile.click();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import { IconCheck, IconKey, IconX } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { FC, KeyboardEvent, useState } from 'react';
|
||||
import { SidebarButton } from '../Sidebar/SidebarButton';
|
||||
|
||||
interface Props {
|
||||
apiKey: string;
|
||||
onApiKeyChange: (apiKey: string) => void;
|
||||
}
|
||||
|
||||
export const Key: FC<Props> = ({ apiKey, onApiKeyChange }) => {
|
||||
const { t } = useTranslation('sidebar');
|
||||
const [isChanging, setIsChanging] = useState(false);
|
||||
const [newKey, setNewKey] = useState(apiKey);
|
||||
|
||||
const handleEnterDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleUpdateKey(newKey);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateKey = (newKey: string) => {
|
||||
onApiKeyChange(newKey.trim());
|
||||
setIsChanging(false);
|
||||
};
|
||||
|
||||
return isChanging ? (
|
||||
<div className="duration:200 flex w-full cursor-pointer items-center rounded-md py-3 px-3 transition-colors hover:bg-gray-500/10">
|
||||
<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-[12.5px] leading-3 text-left text-white outline-none focus:border-neutral-100"
|
||||
type="password"
|
||||
value={newKey}
|
||||
onChange={(e) => setNewKey(e.target.value)}
|
||||
onKeyDown={handleEnterDown}
|
||||
placeholder={t('API Key') || 'API Key'}
|
||||
/>
|
||||
|
||||
<div className="flex w-[40px]">
|
||||
<IconCheck
|
||||
className="ml-auto min-w-[20px] text-neutral-400 hover:text-neutral-100"
|
||||
size={18}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleUpdateKey(newKey);
|
||||
}}
|
||||
/>
|
||||
|
||||
<IconX
|
||||
className="ml-auto min-w-[20px] text-neutral-400 hover:text-neutral-100"
|
||||
size={18}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsChanging(false);
|
||||
setNewKey(apiKey);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<SidebarButton
|
||||
text={t('OpenAI API Key')}
|
||||
icon={<IconKey size={18} />}
|
||||
onClick={() => setIsChanging(true)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user