2023-04-10 00:41:34 +08:00
|
|
|
import React, { useState, useEffect } from 'react';
|
2023-04-04 01:12:14 +08:00
|
|
|
import { useRecoilValue, useRecoilState } from 'recoil';
|
2023-04-07 22:11:28 -04:00
|
|
|
import EditPresetDialog from '../../Endpoints/EditPresetDialog';
|
|
|
|
|
import EndpointItems from './EndpointItems';
|
|
|
|
|
import PresetItems from './PresetItems';
|
2023-04-10 00:41:34 +08:00
|
|
|
import { Trash2 } from 'lucide-react';
|
2023-04-07 22:11:28 -04:00
|
|
|
import FileUpload from './FileUpload';
|
2023-03-31 04:22:16 +08:00
|
|
|
import getIcon from '~/utils/getIcon';
|
2023-04-07 22:11:28 -04:00
|
|
|
import { useDeletePresetMutation, useCreatePresetMutation } from '~/data-provider';
|
2023-03-31 04:22:16 +08:00
|
|
|
import { Button } from '../../ui/Button.tsx';
|
|
|
|
|
import {
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
DropdownMenuContent,
|
|
|
|
|
DropdownMenuLabel,
|
|
|
|
|
DropdownMenuRadioGroup,
|
|
|
|
|
DropdownMenuSeparator,
|
|
|
|
|
DropdownMenuTrigger
|
|
|
|
|
} from '../../ui/DropdownMenu.tsx';
|
2023-04-05 03:50:06 +08:00
|
|
|
import { Dialog, DialogTrigger } from '../../ui/Dialog.tsx';
|
2023-04-07 22:11:28 -04:00
|
|
|
import DialogTemplate from '../../ui/DialogTemplate';
|
2023-03-31 04:22:16 +08:00
|
|
|
|
|
|
|
|
import store from '~/store';
|
|
|
|
|
|
2023-04-02 04:15:07 +08:00
|
|
|
export default function NewConversationMenu() {
|
2023-03-31 04:22:16 +08:00
|
|
|
const [menuOpen, setMenuOpen] = useState(false);
|
2023-04-02 04:15:07 +08:00
|
|
|
const [presetModelVisible, setPresetModelVisible] = useState(false);
|
|
|
|
|
const [preset, setPreset] = useState(false);
|
2023-03-31 04:22:16 +08:00
|
|
|
|
|
|
|
|
const availableEndpoints = useRecoilValue(store.availableEndpoints);
|
2023-04-04 01:12:14 +08:00
|
|
|
const [presets, setPresets] = useRecoilState(store.presets);
|
2023-03-31 04:22:16 +08:00
|
|
|
|
|
|
|
|
const conversation = useRecoilValue(store.conversation) || {};
|
|
|
|
|
const { endpoint, conversationId } = conversation;
|
|
|
|
|
const { newConversation } = store.useConversation();
|
|
|
|
|
|
2023-04-07 22:11:28 -04:00
|
|
|
const deletePresetsMutation = useDeletePresetMutation();
|
|
|
|
|
const createPresetMutation = useCreatePresetMutation();
|
2023-04-04 01:12:14 +08:00
|
|
|
|
2023-04-05 16:16:11 +08:00
|
|
|
const importPreset = jsonData => {
|
2023-04-10 00:41:34 +08:00
|
|
|
createPresetMutation.mutate(
|
|
|
|
|
{ ...jsonData },
|
|
|
|
|
{
|
|
|
|
|
onSuccess: data => {
|
|
|
|
|
setPresets(data);
|
|
|
|
|
},
|
|
|
|
|
onError: error => {
|
|
|
|
|
console.error('Error uploading the preset:', error);
|
|
|
|
|
}
|
2023-04-07 22:11:28 -04:00
|
|
|
}
|
2023-04-10 00:41:34 +08:00
|
|
|
);
|
2023-04-05 16:16:11 +08:00
|
|
|
};
|
|
|
|
|
|
2023-03-31 04:22:16 +08:00
|
|
|
// update the default model when availableModels changes
|
|
|
|
|
// typically, availableModels changes => modelsFilter or customGPTModels changes
|
|
|
|
|
useEffect(() => {
|
2023-04-07 01:49:28 +08:00
|
|
|
const isInvalidConversation = !availableEndpoints.find(e => e === endpoint);
|
|
|
|
|
if (conversationId == 'new' && isInvalidConversation) {
|
2023-03-31 04:22:16 +08:00
|
|
|
newConversation();
|
|
|
|
|
}
|
|
|
|
|
}, [availableEndpoints]);
|
|
|
|
|
|
|
|
|
|
// save selected model to localstoreage
|
|
|
|
|
useEffect(() => {
|
2023-04-11 21:33:14 -04:00
|
|
|
if (endpoint) {
|
|
|
|
|
const lastSelectedModel = JSON.parse(localStorage.getItem('lastSelectedModel')) || {};
|
|
|
|
|
localStorage.setItem('lastConversationSetup', JSON.stringify(conversation));
|
|
|
|
|
localStorage.setItem('lastSelectedModel', JSON.stringify({ ...lastSelectedModel, [endpoint] : conversation.model }));
|
|
|
|
|
}
|
2023-03-31 04:22:16 +08:00
|
|
|
}, [conversation]);
|
|
|
|
|
|
|
|
|
|
// set the current model
|
2023-04-02 04:15:07 +08:00
|
|
|
const onSelectEndpoint = newEndpoint => {
|
2023-03-31 04:22:16 +08:00
|
|
|
setMenuOpen(false);
|
|
|
|
|
|
|
|
|
|
if (!newEndpoint) return;
|
|
|
|
|
else {
|
2023-04-02 04:15:07 +08:00
|
|
|
newConversation({}, { endpoint: newEndpoint });
|
2023-03-31 04:22:16 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-02 04:15:07 +08:00
|
|
|
// set the current model
|
|
|
|
|
const onSelectPreset = newPreset => {
|
|
|
|
|
setMenuOpen(false);
|
|
|
|
|
if (!newPreset) return;
|
|
|
|
|
else {
|
|
|
|
|
newConversation({}, newPreset);
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-03-31 04:22:16 +08:00
|
|
|
|
2023-04-02 04:15:07 +08:00
|
|
|
const onChangePreset = preset => {
|
|
|
|
|
setPresetModelVisible(true);
|
|
|
|
|
setPreset(preset);
|
|
|
|
|
};
|
2023-03-31 04:22:16 +08:00
|
|
|
|
2023-04-07 22:11:28 -04:00
|
|
|
const clearAllPresets = () => {
|
2023-04-10 00:41:34 +08:00
|
|
|
deletePresetsMutation.mutate({ arg: {} });
|
2023-04-07 22:11:28 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onDeletePreset = preset => {
|
2023-04-10 00:41:34 +08:00
|
|
|
deletePresetsMutation.mutate({ arg: preset });
|
2023-04-04 01:12:14 +08:00
|
|
|
};
|
|
|
|
|
|
2023-03-31 04:22:16 +08:00
|
|
|
const icon = getIcon({
|
|
|
|
|
size: 32,
|
|
|
|
|
...conversation,
|
|
|
|
|
isCreatedByUser: false,
|
|
|
|
|
error: false,
|
|
|
|
|
button: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return (
|
2023-04-07 22:11:28 -04:00
|
|
|
<Dialog>
|
2023-03-31 04:22:16 +08:00
|
|
|
<DropdownMenu
|
|
|
|
|
open={menuOpen}
|
|
|
|
|
onOpenChange={setMenuOpen}
|
|
|
|
|
>
|
|
|
|
|
<DropdownMenuTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
2023-04-11 21:33:14 -04:00
|
|
|
className={`group relative mb-[-12px] ml-0 mt-[-8px] items-center rounded-md border-0 p-1 outline-none focus:ring-0 focus:ring-offset-0 dark:data-[state=open]:bg-opacity-50 md:left-1 md:ml-[-12px] md:pl-1`}
|
2023-03-31 04:22:16 +08:00
|
|
|
>
|
|
|
|
|
{icon}
|
2023-04-08 00:14:15 +08:00
|
|
|
<span className="max-w-0 overflow-hidden whitespace-nowrap px-0 text-slate-600 transition-all group-hover:max-w-[80px] group-hover:px-2 group-data-[state=open]:max-w-[80px] group-data-[state=open]:px-2 dark:text-slate-300">
|
|
|
|
|
New Topic
|
|
|
|
|
</span>
|
2023-03-31 04:22:16 +08:00
|
|
|
</Button>
|
|
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
<DropdownMenuContent
|
2023-04-04 01:12:14 +08:00
|
|
|
className="min-w-[300px] dark:bg-gray-700"
|
2023-03-31 04:22:16 +08:00
|
|
|
onCloseAutoFocus={event => event.preventDefault()}
|
|
|
|
|
>
|
2023-04-02 04:15:07 +08:00
|
|
|
<DropdownMenuLabel className="dark:text-gray-300">Select an Endpoint</DropdownMenuLabel>
|
2023-03-31 04:22:16 +08:00
|
|
|
<DropdownMenuSeparator />
|
|
|
|
|
<DropdownMenuRadioGroup
|
|
|
|
|
value={endpoint}
|
2023-04-02 04:15:07 +08:00
|
|
|
onValueChange={onSelectEndpoint}
|
2023-03-31 04:22:16 +08:00
|
|
|
className="overflow-y-auto"
|
|
|
|
|
>
|
|
|
|
|
{availableEndpoints.length ? (
|
|
|
|
|
<EndpointItems
|
|
|
|
|
endpoints={availableEndpoints}
|
2023-04-02 04:15:07 +08:00
|
|
|
onSelect={onSelectEndpoint}
|
2023-03-31 04:22:16 +08:00
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<DropdownMenuLabel className="dark:text-gray-300">No endpoint available.</DropdownMenuLabel>
|
|
|
|
|
)}
|
|
|
|
|
</DropdownMenuRadioGroup>
|
2023-04-02 04:15:07 +08:00
|
|
|
|
|
|
|
|
<div className="mt-6 w-full" />
|
|
|
|
|
|
2023-04-04 01:12:14 +08:00
|
|
|
<DropdownMenuLabel className="flex items-center dark:text-gray-300">
|
|
|
|
|
<span>Select a Preset</span>
|
|
|
|
|
<div className="flex-1" />
|
2023-04-05 16:16:11 +08:00
|
|
|
<FileUpload onFileSelected={importPreset} />
|
2023-04-05 03:50:06 +08:00
|
|
|
<Dialog>
|
|
|
|
|
<DialogTrigger asChild>
|
2023-04-10 00:41:34 +08:00
|
|
|
<label
|
|
|
|
|
htmlFor="file-upload"
|
|
|
|
|
className=" mr-1 flex h-[32px] h-auto cursor-pointer items-center rounded bg-transparent px-2 py-1 text-xs font-medium font-normal text-gray-600 transition-colors hover:bg-slate-200 hover:text-red-700 dark:bg-transparent dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-green-500"
|
|
|
|
|
>
|
|
|
|
|
{/* <Button
|
2023-04-05 03:50:06 +08:00
|
|
|
type="button"
|
|
|
|
|
className="h-auto bg-transparent px-2 py-1 text-xs font-medium font-normal text-red-700 hover:bg-slate-200 hover:text-red-700 dark:bg-transparent dark:text-red-400 dark:hover:bg-gray-800 dark:hover:text-red-400"
|
2023-04-10 00:41:34 +08:00
|
|
|
> */}
|
|
|
|
|
<Trash2 className="mr-1 flex w-[22px] items-center stroke-1" />
|
2023-04-05 03:50:06 +08:00
|
|
|
Clear All
|
2023-04-10 00:41:34 +08:00
|
|
|
{/* </Button> */}
|
|
|
|
|
</label>
|
2023-04-05 03:50:06 +08:00
|
|
|
</DialogTrigger>
|
|
|
|
|
<DialogTemplate
|
|
|
|
|
title="Clear presets"
|
|
|
|
|
description="Are you sure you want to clear all presets? This is irreversible."
|
|
|
|
|
selection={{
|
2023-04-07 22:11:28 -04:00
|
|
|
selectHandler: clearAllPresets,
|
2023-04-05 03:50:06 +08:00
|
|
|
selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white',
|
|
|
|
|
selectText: 'Clear'
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</Dialog>
|
2023-04-04 01:12:14 +08:00
|
|
|
</DropdownMenuLabel>
|
2023-04-02 04:15:07 +08:00
|
|
|
<DropdownMenuSeparator />
|
|
|
|
|
<DropdownMenuRadioGroup
|
|
|
|
|
onValueChange={onSelectPreset}
|
|
|
|
|
className="overflow-y-auto"
|
|
|
|
|
>
|
|
|
|
|
{presets.length ? (
|
|
|
|
|
<PresetItems
|
|
|
|
|
presets={presets}
|
|
|
|
|
onSelect={onSelectPreset}
|
|
|
|
|
onChangePreset={onChangePreset}
|
2023-04-07 22:11:28 -04:00
|
|
|
onDeletePreset={onDeletePreset}
|
2023-04-02 04:15:07 +08:00
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<DropdownMenuLabel className="dark:text-gray-300">No preset yet.</DropdownMenuLabel>
|
|
|
|
|
)}
|
|
|
|
|
</DropdownMenuRadioGroup>
|
2023-03-31 04:22:16 +08:00
|
|
|
</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
2023-04-02 04:15:07 +08:00
|
|
|
<EditPresetDialog
|
|
|
|
|
open={presetModelVisible}
|
|
|
|
|
onOpenChange={setPresetModelVisible}
|
|
|
|
|
preset={preset}
|
|
|
|
|
/>
|
2023-03-31 04:22:16 +08:00
|
|
|
</Dialog>
|
|
|
|
|
);
|
|
|
|
|
}
|