chore: model menu functions behave as expected

This commit is contained in:
Danny Avila 2023-03-07 13:19:56 -05:00
parent 191118b90b
commit 6be037323e
9 changed files with 157 additions and 35 deletions

View file

@ -1,7 +1,7 @@
import React from 'react';
import ModelItem from './ModelItem';
export default function MenuItems({ models }) {
export default function MenuItems({ models, onSelect }) {
return (
<>
{models.map((modelItem, i) => (
@ -9,6 +9,7 @@ export default function MenuItems({ models }) {
key={i}
modelName={modelItem.name}
value={modelItem.value}
onSelect={onSelect}
/>
))}
</>

View file

@ -1,6 +1,6 @@
import React, { useState, useRef } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import { useDispatch } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
import { setModel, setCustomGpt } from '~/store/submitSlice';
import manualSWR from '~/utils/fetchers';
import { Button } from '../ui/Button.tsx';
@ -16,8 +16,9 @@ import {
DialogTitle
} from '../ui/Dialog.tsx';
export default function ModelDialog({ mutate, modelMap, setModelSave, handleSaveState }) {
export default function ModelDialog({ mutate, setModelSave, handleSaveState }) {
const dispatch = useDispatch();
const { modelMap, initial } = useSelector((state) => state.models);
const [chatGptLabel, setChatGptLabel] = useState('');
const [promptPrefix, setPromptPrefix] = useState('');
const [saveText, setSaveText] = useState('Save');
@ -65,9 +66,12 @@ export default function ModelDialog({ mutate, modelMap, setModelSave, handleSave
if (
chatGptLabel !== 'chatgptCustom' &&
modelMap[chatGptLabel.toLowerCase()] &&
!initial[chatGptLabel.toLowerCase()] &&
saveText === 'Save'
) {
setSaveText('Update');
} else if (!modelMap[chatGptLabel.toLowerCase()] && saveText === 'Update') {
setSaveText('Save');
}
const requiredProp = required ? { required: true } : {};

View file

@ -1,13 +1,22 @@
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import { useSelector } from 'react-redux';
import { DropdownMenuRadioItem } from '../ui/DropdownMenu.tsx';
import { Circle } from 'lucide-react';
import { DialogTrigger } from '../ui/Dialog.tsx';
import RenameButton from '../Conversations/RenameButton';
import TrashIcon from '../svg/TrashIcon';
import manualSWR from '~/utils/fetchers';
export default function ModelItem({ modelName, value }) {
export default function ModelItem({ modelName, value, onSelect }) {
const { customModel } = useSelector((state) => state.submit);
const { initial } = useSelector((state) => state.models);
const [isHovering, setIsHovering] = useState(false);
const [renaming, setRenaming] = useState(false);
const [currentName, setCurrentName] = useState(modelName);
const [modelInput, setModelInput] = useState(modelName);
const inputRef = useRef(null);
const rename = manualSWR(`http://localhost:3080/api/customGpts`, 'post');
const deleteCustom = manualSWR(`http://localhost:3080/api/customGpts/delete`, 'post');
if (value === 'chatgptCustom') {
return (
@ -23,6 +32,18 @@ export default function ModelItem({ modelName, value }) {
);
}
if (initial[value]) {
return (
<DropdownMenuRadioItem
value={value}
className="dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
>
{modelName}
{value === 'chatgpt' && <sup>$</sup>}
</DropdownMenuRadioItem>
);
}
const handleMouseOver = () => {
setIsHovering(true);
};
@ -31,31 +52,101 @@ export default function ModelItem({ modelName, value }) {
setIsHovering(false);
};
const renameHandler = (e) => {
e.preventDefault();
e.stopPropagation();
setRenaming(true);
setTimeout(() => {
inputRef.current.focus();
}, 25);
};
const onRename = (e) => {
e.preventDefault();
setRenaming(false);
if (modelInput === modelName) {
return;
}
rename.trigger({
prevLabel: currentName,
chatGptLabel: modelInput,
value: modelInput.toLowerCase()
});
setCurrentName(modelInput);
};
const onDelete = async (e) => {
e.preventDefault();
await deleteCustom.trigger({ value: currentName.toLowerCase() });
// await mutate();
onSelect('chatgpt', true);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
onRename(e);
}
};
const buttonClass = {
className:
'rounded-md m-0 text-gray-400 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'
'z-50 rounded-md m-0 text-gray-400 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'
};
const itemClass = {
className:
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm font-medium outline-none hover:bg-slate-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:hover:bg-slate-700 dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800'
};
const showButtons = isHovering && !initial[value];
return (
<DropdownMenuRadioItem
<span
value={value}
className="dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
className={itemClass.className}
onClick={(e) => {
onSelect(value, true);
}}
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
>
{modelName}
{value === 'chatgpt' && <sup>$</sup>}
{customModel === value && (
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<Circle className="h-2 w-2 fill-current" />
</span>
)}
{renaming === true ? (
<input
ref={inputRef}
type="text"
className="pointer-events-auto z-50 m-0 mr-2 w-3/4 border border-blue-500 bg-transparent p-0 text-sm leading-tight outline-none"
value={modelInput}
onClick={(e) => e.stopPropagation()}
onChange={(e) => setModelInput(e.target.value)}
onBlur={onRename}
onKeyDown={handleKeyDown}
/>
) : (
modelInput
)}
{value === 'chatgpt' && <sup>$</sup>}
{showButtons && (
<>
<RenameButton twcss={`ml-auto mr-2 ${buttonClass.className}`} />
<button {...buttonClass}>
<RenameButton
twcss={`ml-auto mr-2 ${buttonClass.className}`}
onRename={onRename}
renaming={renaming}
renameHandler={renameHandler}
/>
<button
{...buttonClass}
onClick={onDelete}
>
<TrashIcon />
</button>
</>
)}
</DropdownMenuRadioItem>
</span>
);
}

View file

@ -24,13 +24,10 @@ import { Dialog } from '../ui/Dialog.tsx';
export default function ModelMenu() {
const dispatch = useDispatch();
const [modelSave, setModelSave] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const { model, customModel } = useSelector((state) => state.submit);
const { models, modelMap, initial } = useSelector((state) => state.models);
const { trigger } = manualSWR(`http://localhost:3080/api/customGpts`, 'get', (res) => {
if (models.length + res.length === models.length) {
return;
}
const fetchedModels = res.map((modelItem) => ({
...modelItem,
name: modelItem.chatGptLabel
@ -53,7 +50,7 @@ export default function ModelMenu() {
localStorage.setItem('model', JSON.stringify(model));
}, [model]);
const onChange = (value) => {
const onChange = (value, custom = false) => {
if (!value) {
return;
} else if (value === 'chatgptCustom') {
@ -62,12 +59,18 @@ export default function ModelMenu() {
dispatch(setModel(value));
dispatch(setDisabled(false));
dispatch(setCustomModel(null));
if (custom) {
trigger();
}
} else if (!initial[value]) {
const chatGptLabel = modelMap[value]?.chatGptLabel;
const promptPrefix = modelMap[value]?.promptPrefix;
dispatch(setCustomGpt({ chatGptLabel, promptPrefix }));
dispatch(setModel('chatgptCustom'));
dispatch(setCustomModel(value));
if (custom) {
setMenuOpen((prevOpen) => !prevOpen);
}
} else if (!modelMap[value]) {
dispatch(setCustomModel(null));
}
@ -101,7 +104,9 @@ export default function ModelMenu() {
const defaultColorProps = [
'text-gray-500',
'hover:bg-gray-100',
'hover:bg-opacity-20',
'disabled:hover:bg-transparent',
'dark:data-[state=open]:bg-gray-800',
'dark:hover:bg-opacity-20',
'dark:hover:bg-gray-900',
'dark:hover:text-gray-400',
@ -110,9 +115,11 @@ export default function ModelMenu() {
const chatgptColorProps = [
'text-green-700',
'data-[state=open]:bg-green-100',
'dark:text-emerald-300',
'hover:bg-green-100',
'disabled:hover:bg-transparent',
'dark:data-[state=open]:bg-green-900',
'dark:hover:bg-opacity-50',
'dark:hover:bg-green-900',
'dark:hover:text-gray-100',
@ -124,14 +131,17 @@ export default function ModelMenu() {
return (
<Dialog onOpenChange={onOpenChange}>
<DropdownMenu>
<DropdownMenu
open={menuOpen}
onOpenChange={setMenuOpen}
>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
// style={{backgroundColor: 'rgb(16, 163, 127)'}}
className={`absolute bottom-0.5 rounded-md border-0 p-1 pl-2 outline-none ${colorProps.join(
' '
)} focus:ring-0 focus:ring-offset-0 disabled:bottom-0.5 dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:bg-opacity-50 md:bottom-1 md:left-2 md:pl-1 md:disabled:bottom-1`}
)} focus:ring-0 focus:ring-offset-0 disabled:bottom-0.5 dark:data-[state=open]:bg-opacity-50 md:bottom-1 md:left-2 md:pl-1 md:disabled:bottom-1`}
>
{icon}
</Button>
@ -140,11 +150,14 @@ export default function ModelMenu() {
<DropdownMenuLabel className="dark:text-gray-300">Select a Model</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup
value={customModel?.length > 0 ? customModel : model}
value={customModel ? customModel : model}
onValueChange={onChange}
className="overflow-y-auto"
>
<MenuItems models={models} />
<MenuItems
models={models}
onSelect={onChange}
/>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>