mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-03 00:58:50 +01:00
refactor(types): use zod for better type safety, style(Messages): new scroll behavior, style(Buttons): match ChatGPT (#761)
* feat: add zod schemas for better type safety * refactor(useSetOptions): remove 'as Type' in favor of zod schema * fix: descendant console error, change <p> tag to <div> tag for content in PluginTooltip component * style(MessagesView): instant/snappier scroll behavior matching official site * fix(Messages): add null check for scrollableRef before accessing its properties in handleScroll and useEffect * fix(messageSchema.js): change type of invocationId from string to number fix(schemas.ts): make authenticated property in tPluginSchema optional fix(schemas.ts): make isButton property in tPluginSchema optional fix(schemas.ts): make messages property in tConversationSchema optional and change its type to array of strings fix(schemas.ts): make systemMessage property in tConversationSchema nullable and optional fix(schemas.ts): make modelLabel property in tConversationSchema nullable and optional fix(schemas.ts): make chatGptLabel property in tConversationSchema nullable and optional fix(schemas.ts): make promptPrefix property in tConversationSchema nullable and optional fix(schemas.ts): make context property in tConversationSchema nullable and optional fix(schemas.ts): make jailbreakConversationId property in tConversationSchema nullable and optional fix(schemas.ts): make conversationSignature property in tConversationSchema nullable and optional fix(schemas.ts): make clientId property * refactor(types): replace main types with zod schemas and inferred types * refactor(types/schemas): use schemas for better type safety of main types * style(ModelSelect/Buttons): remove shadow and transition * style(ModelSelect): button changes to closer match OpenAI * style(ModelSelect): remove green rings which flicker * style(scrollToBottom): add two separate scrolling functions * fix(OptionsBar.tsx): handle onFocus and onBlur events to update opacityClass fix(Messages/index.jsx): increase debounce time for scrollIntoView function
This commit is contained in:
parent
173b8ce2da
commit
5828200197
30 changed files with 329 additions and 317 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import exportFromJSON from 'export-from-json';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue, useRecoilState } from 'recoil';
|
||||
import { EditPresetProps, SetOption, TPreset } from 'librechat-data-provider';
|
||||
import { EditPresetProps, SetOption, tPresetSchema } from 'librechat-data-provider';
|
||||
import { Dialog, DialogButton } from '~/components/ui';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import SaveAsPresetDialog from './SaveAsPresetDialog';
|
||||
|
|
@ -21,12 +21,11 @@ const EndpointOptionsDialog = ({ open, onOpenChange, preset: _preset, title }: E
|
|||
const setOption: SetOption = (param) => (newValue) => {
|
||||
const update = {};
|
||||
update[param] = newValue;
|
||||
setPreset(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
...update,
|
||||
} as TPreset),
|
||||
setPreset((prevState) =>
|
||||
tPresetSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useCreatePresetMutation, EditPresetProps, TPreset } from 'librechat-data-provider';
|
||||
import { useCreatePresetMutation, EditPresetProps } from 'librechat-data-provider';
|
||||
import { Dialog, Input, Label } from '~/components/ui/';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { cn, defaultTextPropsLabel, removeFocusOutlines, cleanupPreset } from '~/utils/';
|
||||
|
|
@ -20,7 +20,7 @@ const SaveAsPresetDialog = ({ open, onOpenChange, preset }: EditPresetProps) =>
|
|||
title,
|
||||
},
|
||||
endpointsConfig,
|
||||
}) as TPreset;
|
||||
});
|
||||
createPresetMutation.mutate(_preset);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -68,14 +68,14 @@ function Examples({ readonly, examples, setExample, addExample, removeExample }:
|
|||
<div className="flex justify-center">
|
||||
<Button
|
||||
type="button"
|
||||
className="mr-2 mt-1 h-auto items-center justify-center bg-transparent px-3 py-2 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-600 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
className="mr-2 mt-1 h-auto items-center justify-center bg-transparent px-3 py-2 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
onClick={removeExample}
|
||||
>
|
||||
<Minus className="w-[16px]" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
className="mt-1 h-auto items-center justify-center bg-transparent px-3 py-2 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-600 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
className="mt-1 h-auto items-center justify-center bg-transparent px-3 py-2 text-xs font-medium font-normal text-black hover:bg-slate-200 hover:text-black focus:ring-0 focus:ring-offset-0 dark:bg-transparent dark:text-white dark:hover:bg-gray-700 dark:hover:text-white dark:focus:outline-none dark:focus:ring-offset-0"
|
||||
onClick={addExample}
|
||||
>
|
||||
<Plus className="w-[16px]" />
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default function Anthropic({ conversation, setOption, models }: ModelSele
|
|||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-48 z-50 flex h-[40px] w-48 flex-none items-center justify-center px-4 ring-0 transition duration-700 ease-in-out hover:cursor-pointer hover:bg-slate-50 hover:shadow-md focus:ring-0 focus:ring-offset-0 data-[state=open]:bg-slate-50 dark:bg-gray-700 dark:hover:bg-gray-600 dark:data-[state=open]:bg-gray-600',
|
||||
'min-w-48 z-50 flex h-[40px] w-48 flex-none items-center justify-center px-4 ring-0 hover:cursor-pointer',
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -34,15 +34,15 @@ export default function BingAI({ conversation, setOption, models }: ModelSelectP
|
|||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'z-50 flex h-[40px] w-36 flex-none items-center justify-center px-4 ring-0 transition duration-700 ease-in-out hover:cursor-pointer hover:bg-slate-50 hover:shadow-md focus:ring-0 focus:ring-offset-0 data-[state=open]:bg-slate-50 dark:bg-gray-700 dark:hover:bg-gray-600 dark:data-[state=open]:bg-gray-600',
|
||||
'z-50 flex h-[40px] w-36 flex-none items-center justify-center px-4 ring-0 hover:cursor-pointer hover:bg-slate-50 focus:ring-0 focus:ring-offset-0 data-[state=open]:bg-slate-50 dark:bg-gray-800 dark:hover:bg-gray-700 dark:data-[state=open]:bg-gray-600',
|
||||
showBingToneSetting ? 'hidden' : '',
|
||||
)}
|
||||
/>
|
||||
<Tabs
|
||||
value={toneStyle}
|
||||
value={toneStyle ?? 'creative'}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'z-50 flex h-[40px] flex-none items-center justify-center px-0 transition duration-700 ease-in-out hover:bg-slate-50 hover:shadow-md dark:hover:bg-gray-600',
|
||||
'z-50 flex h-[40px] flex-none items-center justify-center px-0 hover:bg-slate-50 dark:hover:bg-gray-700',
|
||||
)}
|
||||
onValueChange={(value) => setOption('toneStyle')(value.toLowerCase())}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default function ChatGPT({ conversation, setOption, models }: ModelSelect
|
|||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-48 z-50 flex h-[40px] w-60 flex-none items-center justify-center px-4 ring-0 transition duration-700 ease-in-out hover:cursor-pointer hover:bg-slate-50 hover:shadow-md focus:ring-0 focus:ring-offset-0 data-[state=open]:bg-slate-50 dark:bg-gray-700 dark:hover:bg-gray-600 dark:data-[state=open]:bg-gray-600',
|
||||
'min-w-48 z-50 flex h-[40px] w-60 flex-none items-center justify-center px-4 ring-0 hover:cursor-pointer',
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default function Google({ conversation, setOption, models }: ModelSelectP
|
|||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-48 z-50 flex h-[40px] w-48 flex-none items-center justify-center px-4 ring-0 transition duration-700 ease-in-out hover:cursor-pointer hover:bg-slate-50 hover:shadow-md focus:ring-0 focus:ring-offset-0 data-[state=open]:bg-slate-50 dark:bg-gray-700 dark:hover:bg-gray-600 dark:data-[state=open]:bg-gray-600',
|
||||
'min-w-48 z-50 flex h-[40px] w-48 flex-none items-center justify-center px-4 ring-0 hover:cursor-pointer',
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default function OpenAI({ conversation, setOption, models }: ModelSelectP
|
|||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-48 z-50 flex h-[40px] w-48 flex-none items-center justify-center px-4 ring-0 transition duration-700 ease-in-out hover:cursor-pointer hover:bg-slate-50 hover:shadow-md focus:ring-0 focus:ring-offset-0 data-[state=open]:bg-slate-50 dark:bg-gray-700 dark:hover:bg-gray-600 dark:data-[state=open]:bg-gray-600',
|
||||
'min-w-48 z-50 flex h-[40px] w-48 flex-none items-center justify-center px-4 hover:cursor-pointer',
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export default function Plugins({ conversation, setOption, models }: ModelSelect
|
|||
}
|
||||
|
||||
const tools = [...user.plugins]
|
||||
.map((el) => allPlugins.find((plugin) => plugin.pluginKey === el))
|
||||
.map((el) => allPlugins.find((plugin: TPlugin) => plugin.pluginKey === el))
|
||||
.filter((el): el is TPlugin => el !== undefined);
|
||||
|
||||
/* Filter Last Selected Tools */
|
||||
|
|
@ -71,7 +71,7 @@ export default function Plugins({ conversation, setOption, models }: ModelSelect
|
|||
type="button"
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-4 z-40 flex h-[40px] flex-none items-center justify-center px-3 transition duration-700 ease-in-out hover:bg-white hover:shadow-md focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700',
|
||||
'min-w-4 z-40 flex h-[40px] flex-none items-center justify-center px-3 hover:bg-white focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700',
|
||||
)}
|
||||
onClick={() => setVisibility((prev) => !prev)}
|
||||
>
|
||||
|
|
@ -87,11 +87,7 @@ export default function Plugins({ conversation, setOption, models }: ModelSelect
|
|||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-60 z-40 flex w-64 transition duration-700 ease-in-out hover:shadow-md sm:w-48',
|
||||
visible ? '' : 'hidden',
|
||||
)}
|
||||
className={cn(cardStyle, 'min-w-60 z-40 flex w-64 sm:w-48', visible ? '' : 'hidden')}
|
||||
/>
|
||||
<MultiSelectDropDown
|
||||
value={conversation.tools || []}
|
||||
|
|
@ -100,11 +96,7 @@ export default function Plugins({ conversation, setOption, models }: ModelSelect
|
|||
availableValues={availableTools}
|
||||
optionValueKey="pluginKey"
|
||||
showAbove={true}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-60 z-50 w-64 transition duration-700 ease-in-out hover:shadow-md sm:w-48',
|
||||
visible ? '' : 'hidden',
|
||||
)}
|
||||
className={cn(cardStyle, 'min-w-60 z-50 w-64 sm:w-48', visible ? '' : 'hidden')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Settings2 } from 'lucide-react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { TPreset } from 'librechat-data-provider';
|
||||
import { tPresetSchema } from 'librechat-data-provider';
|
||||
import { PluginStoreDialog } from '~/components';
|
||||
import {
|
||||
EndpointSettings,
|
||||
|
|
@ -98,6 +98,21 @@ export default function OptionsBar() {
|
|||
}
|
||||
setOpacityClass('show');
|
||||
}}
|
||||
onFocus={() => {
|
||||
if (showPopover) {
|
||||
return;
|
||||
}
|
||||
setOpacityClass('full-opacity');
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (showPopover) {
|
||||
return;
|
||||
}
|
||||
if (!messagesTree || messagesTree.length === 0) {
|
||||
return;
|
||||
}
|
||||
setOpacityClass('show');
|
||||
}}
|
||||
>
|
||||
<ModelSelect conversation={conversation} setOption={setOption} />
|
||||
{!noSettings[endpoint] && (
|
||||
|
|
@ -105,7 +120,7 @@ export default function OptionsBar() {
|
|||
type="button"
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-4 z-50 flex h-[40px] flex-none items-center justify-center px-3 transition duration-700 ease-in-out hover:bg-slate-50 hover:shadow-md focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-600',
|
||||
'min-w-4 z-50 flex h-[40px] flex-none items-center justify-center px-3 focus:ring-0 focus:ring-offset-0',
|
||||
)}
|
||||
onClick={triggerAdvancedMode}
|
||||
>
|
||||
|
|
@ -126,7 +141,7 @@ export default function OptionsBar() {
|
|||
<SaveAsPresetDialog
|
||||
open={saveAsDialogShow}
|
||||
onOpenChange={setSaveAsDialogShow}
|
||||
preset={{ ...conversation } as TPreset}
|
||||
preset={tPresetSchema.parse({ ...conversation })}
|
||||
/>
|
||||
<PluginStoreDialog isOpen={showPluginStoreDialog} setIsOpen={setShowPluginStoreDialog} />
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ export default function Messages({ isSearchView = false }) {
|
|||
const { screenshotTargetRef } = useScreenshot();
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!scrollableRef.current) {
|
||||
return;
|
||||
}
|
||||
const { scrollTop, scrollHeight, clientHeight } = scrollableRef.current;
|
||||
const diff = Math.abs(scrollHeight - scrollTop);
|
||||
const percent = Math.abs(clientHeight - diff) / clientHeight;
|
||||
|
|
@ -40,6 +43,9 @@ export default function Messages({ isSearchView = false }) {
|
|||
|
||||
useEffect(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
if (!scrollableRef.current) {
|
||||
return;
|
||||
}
|
||||
const { scrollTop, scrollHeight, clientHeight } = scrollableRef.current;
|
||||
const diff = Math.abs(scrollHeight - scrollTop);
|
||||
const percent = Math.abs(clientHeight - diff) / clientHeight;
|
||||
|
|
@ -58,6 +64,19 @@ export default function Messages({ isSearchView = false }) {
|
|||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const scrollToBottom = useCallback(
|
||||
throttle(
|
||||
() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'instant' });
|
||||
setShowScrollButton(false);
|
||||
},
|
||||
450,
|
||||
{ leading: true },
|
||||
),
|
||||
[messagesEndRef],
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const scrollToBottomSmooth = useCallback(
|
||||
throttle(
|
||||
() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
|
|
@ -77,7 +96,7 @@ export default function Messages({ isSearchView = false }) {
|
|||
|
||||
const scrollHandler = (e) => {
|
||||
e.preventDefault();
|
||||
scrollToBottom();
|
||||
scrollToBottomSmooth();
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
useUpdateUserPluginsMutation,
|
||||
TPlugin,
|
||||
TPluginAction,
|
||||
TConversation,
|
||||
tConversationSchema,
|
||||
TError,
|
||||
} from 'librechat-data-provider';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
|
|
@ -69,12 +69,11 @@ function PluginStoreDialog({ isOpen, setIsOpen }: TPluginStoreDialogProps) {
|
|||
return t.pluginKey !== plugin;
|
||||
});
|
||||
localStorage.setItem('lastSelectedTools', JSON.stringify(tools));
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
tools,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
tools,
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ function PluginTooltip({ content, position }: TPluginTooltipProps) {
|
|||
<HoverCardPortal>
|
||||
<HoverCardContent side={position} className="w-80 ">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
<div className="text-sm text-gray-600 dark:text-gray-300">
|
||||
<div dangerouslySetInnerHTML={{ __html: content }} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function Dropdown({ value, onChange, options, className, containerClassName }) {
|
|||
<Listbox value={value} onChange={onChange}>
|
||||
<Listbox.Button
|
||||
className={cn(
|
||||
'relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:border-green-600 focus:outline-none focus:ring-1 focus:ring-green-600 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
|
||||
'relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-1 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
|
||||
className || '',
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ function MultiSelectDropDown({
|
|||
<>
|
||||
<Listbox.Button
|
||||
className={cn(
|
||||
'relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:border-green-600 focus:outline-none focus:ring-1 focus:ring-green-600 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
|
||||
'relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
|
||||
className ?? '',
|
||||
)}
|
||||
id={excludeIds[0]}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function SelectDropDown({
|
|||
<>
|
||||
<Listbox.Button
|
||||
className={cn(
|
||||
'relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:border-green-600 focus:outline-none focus:ring-1 focus:ring-green-600 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
|
||||
'relative flex w-full cursor-default flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-white/20 dark:bg-gray-800 sm:text-sm',
|
||||
className ?? '',
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const Tabs = TabsPrimitive.Root;
|
|||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className = '', ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
|
|
@ -25,7 +25,7 @@ TabsList.displayName = TabsPrimitive.List.displayName;
|
|||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className = '', ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
className={cn(
|
||||
'inline-flex min-w-[100px] items-center justify-center rounded-[0.185rem] px-3 py-1.5 text-sm font-medium text-gray-700 transition-all disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-white data-[state=active]:text-gray-900 data-[state=active]:shadow-sm dark:text-gray-200 dark:data-[state=active]:bg-gray-700 dark:data-[state=active]:text-gray-100',
|
||||
|
|
@ -40,7 +40,7 @@ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className = '', ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
className={cn('mt-2 rounded-md border border-gray-200 p-6 dark:border-gray-700', className)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -13,15 +13,14 @@ const usePresetOptions: UsePresetOptions = (_preset) => {
|
|||
const setOption: SetOption = (param) => (newValue) => {
|
||||
const update = {};
|
||||
update[param] = newValue;
|
||||
setPreset(
|
||||
(prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}) as TPreset,
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -32,15 +31,14 @@ const usePresetOptions: UsePresetOptions = (_preset) => {
|
|||
currentExample[type] = { content: newValue };
|
||||
current[i] = currentExample;
|
||||
update['examples'] = current;
|
||||
setPreset(
|
||||
(prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}) as TPreset,
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -49,15 +47,14 @@ const usePresetOptions: UsePresetOptions = (_preset) => {
|
|||
const current = preset?.examples?.slice() || [];
|
||||
current.push({ input: { content: '' }, output: { content: '' } });
|
||||
update['examples'] = current;
|
||||
setPreset(
|
||||
(prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}) as TPreset,
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -66,29 +63,27 @@ const usePresetOptions: UsePresetOptions = (_preset) => {
|
|||
const current = preset?.examples?.slice() || [];
|
||||
if (current.length <= 1) {
|
||||
update['examples'] = [{ input: { content: '' }, output: { content: '' } }];
|
||||
setPreset(
|
||||
(prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}) as TPreset,
|
||||
);
|
||||
return;
|
||||
}
|
||||
current.pop();
|
||||
update['examples'] = current;
|
||||
setPreset(
|
||||
(prevState) =>
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}) as TPreset,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
current.pop();
|
||||
update['examples'] = current;
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
endpointsConfig,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -96,15 +91,14 @@ const usePresetOptions: UsePresetOptions = (_preset) => {
|
|||
const editablePreset = JSON.parse(JSON.stringify(_preset));
|
||||
const { agentOptions } = editablePreset;
|
||||
agentOptions[param] = newValue;
|
||||
setPreset(
|
||||
(prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
agentOptions,
|
||||
},
|
||||
endpointsConfig,
|
||||
}) as TPreset,
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
agentOptions,
|
||||
},
|
||||
endpointsConfig,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
SetOption,
|
||||
SetExample,
|
||||
TPlugin,
|
||||
tConversationSchema,
|
||||
} from 'librechat-data-provider';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import usePresetOptions from './usePresetOptions';
|
||||
|
|
@ -23,12 +24,11 @@ const useSetOptions: UseSetOptions = (preset = false) => {
|
|||
const setOption: SetOption = (param) => (newValue) => {
|
||||
const update = {};
|
||||
update[param] = newValue;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
...update,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -39,12 +39,11 @@ const useSetOptions: UseSetOptions = (preset = false) => {
|
|||
currentExample[type] = { content: newValue };
|
||||
current[i] = currentExample;
|
||||
update['examples'] = current;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
...update,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -53,12 +52,11 @@ const useSetOptions: UseSetOptions = (preset = false) => {
|
|||
const current = conversation?.examples?.slice() || [];
|
||||
current.push({ input: { content: '' }, output: { content: '' } });
|
||||
update['examples'] = current;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
...update,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -67,23 +65,21 @@ const useSetOptions: UseSetOptions = (preset = false) => {
|
|||
const current = conversation?.examples?.slice() || [];
|
||||
if (current.length <= 1) {
|
||||
update['examples'] = [{ input: { content: '' }, output: { content: '' } }];
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
...update,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
current.pop();
|
||||
update['examples'] = current;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
...update,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -101,12 +97,11 @@ const useSetOptions: UseSetOptions = (preset = false) => {
|
|||
const convo = JSON.parse(editableConvo);
|
||||
const { agentOptions } = convo;
|
||||
agentOptions[param] = newValue;
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
agentOptions,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
agentOptions,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -128,12 +123,11 @@ const useSetOptions: UseSetOptions = (preset = false) => {
|
|||
}
|
||||
|
||||
localStorage.setItem('lastSelectedTools', JSON.stringify(update['tools']));
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
({
|
||||
...prevState,
|
||||
...update,
|
||||
} as TConversation),
|
||||
setConversation((prevState) =>
|
||||
tConversationSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { CleanupPreset } from 'librechat-data-provider';
|
||||
import { CleanupPreset, TPreset } from 'librechat-data-provider';
|
||||
|
||||
const cleanupPreset = ({ preset: _preset, endpointsConfig = {} }: CleanupPreset) => {
|
||||
const cleanupPreset = ({ preset: _preset, endpointsConfig = {} }: CleanupPreset): TPreset => {
|
||||
const { endpoint } = _preset;
|
||||
|
||||
let preset = {};
|
||||
let preset = {} as TPreset;
|
||||
let models = [];
|
||||
if (endpoint) {
|
||||
models = endpointsConfig[endpoint]?.availableModels || [];
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export const removeFocusOutlines =
|
|||
'focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0';
|
||||
|
||||
export const cardStyle =
|
||||
'transition-colors rounded-md min-w-[75px] font-normal bg-white border-black/10 hover:border-black/10 focus:border-black/10 dark:border-black/10 dark:hover:border-black/10 dark:focus:border-black/10 border dark:bg-gray-700 text-black dark:text-white';
|
||||
'transition-colors rounded-md min-w-[75px] border font-normal bg-white hover:bg-slate-50 dark:border-gray-600 dark:hover:bg-gray-700 dark:bg-gray-800 text-black dark:text-gray-600 focus:outline-none data-[state=open]:bg-slate-50 dark:data-[state=open]:bg-gray-700';
|
||||
|
||||
export const defaultTextProps =
|
||||
'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue