mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 18:00:15 +01:00
refactor: Settings/Presets UI Restructure, convert many files to TS (#740)
* progress on settings refactor * fix(helpers.js): replace fs.rmdirSync with fs.rm to delete node_modules directory recursively fix(packages.js): delete package-lock.json if it exists before running the script * feat(CrossIcon.tsx): add CrossIcon component * wip: refactor Options for modularity into higher order components, OptionsBar > ModelSelect/Settings * refactor: import more from utils/index, including cardStyle used by model select/settings * refactor(AnthropicOptions): refactor to new format, OpenAI: reduce format to name of endpoint * refactor(AnthropicSettings): refactor to new format, match defaults to API docs * fix: google and anthropic defaults * refactor(conversation/submission atoms): add typing, remove unused code * chore(types.ts): add missing type definitions for TMessages, TMessagesAtom, TConversationAtom, and ModelSelectProps feat(types.ts): make endpoint property nullable in TSubmission, TEndpointOption, TConversation, and TPreset types * refactor(ChatGPT): refactor to new format, add omit settings logic * refactor(EndpointSettings/BingAI): new dir structure and format BingAI options/settings to new * fix: update useUpdateTokenCountMutation to accept an object with a 'text' property instead of a string * fix(endpoints): ensure expected behaviors for preset dialogs * chore(index.ts): add defaultTextProps to utils/index.ts for use in settings components * chore(index.ts): add optionText to utils/index.ts for use in settings components * wip: refactor google settings * wip: progress with Google refactor, needs AdditionalButtons handling and global state setters * refactor(OptionsBar.tsx): The setOption function has been refactored to use the useSetOptions custom hook for setting conversation options. * chore(Anthropic.tsx, BingAI.tsx, Google.tsx, OpenAI.tsx): adjust height of container div in Settings component; chore(Examples.tsx): adjust height in Examples component * refactor(Google): complete google refactor feat(client): add new component PopoverButtons for displaying popover buttons in EndpointPopover feat(data-provider): add types for PopoverButton and EndpointOptionsPopoverProps * fix(OptionsBar.tsx): add useEffect hook to handle opacity class based on messagesTree and advancedMode fix(style.css): rename class from 'openAIOptions-simple-container' to 'options-bar' and update references * refactor(Plugins/OptionsBar): complete refactor of Plugins Select options, consolidate logic from TextChat to OptionsBar * fix(Plugins.tsx): filter lastSelectedTools to remove any tools that are not in the current tools list fix(useSetOptions.ts): remove unnecessary empty line * feat(useSetOptions.ts): add setAgentOption function to update agentOptions in conversation state feat(types.ts): add setAgentOption function to UseSetOptions type * refactor(Settings/Plugins): refactor to new format, refactor(OptionHover): use same component for all endpoints * refactor(OptionHover.tsx): refactor types object to use nested objects for openAI and gptPlugins feat(OptionHover.tsx): add openAI object with specific properties for openAI configuration * refactor(AgentSettings): new format, feat(types.ts): add TAgentOptions type for defining agent options in a conversation * feat(PopoverButtons.tsx): add support for GPT plugin settings button feat(Plugins.tsx): create PluginsView component for displaying plugin settings feat(optionSettings.ts): add showAgentSettings atom for controlling agent settings visibility * feat(client): add support for PluginsSettings in Input/Settings component fix(client): change import path for PluginsSettings in Input/Settings component * refactor(Settings/Plugins): complete refactor, store: refactor to TS, refactor: import defaultTextPropsLabel from utils * feat(EndpointSettings, AgentSettings, Anthropic, Google, types.ts): Add support for Recoil state management and useRecoilValue hook; Pass models from endpointsConfig to various components; Add TModels type and update ModelSelectProps type. fix(AgentSettings, Anthropic, Google, GoogleView, Plugins, OpenAI, Settings.tsx): Change import statements for ModelSelectProps from librechat-data-provider; Add models as a parameter to various components; Add models prop to PluginsView, Settings, and other components. * refactor(EditPresetDialog.jsx): update import statements for Examples and AgentSettings components feat(Settings/index.ts): add export statements for Examples and AgentSettings components * chore(package.json): update eslint-plugin-import to version 2.28.0 * fix(eslint): dependency cycle rule is now working * fix: dependency cycle errors and type errors * refactor(EditPresetDialog.jsx): update import path for DialogTemplate component refactor(NewConversationMenu/index.jsx): update import path for DialogTemplate component refactor(ExportModel.jsx): update import path for DialogTemplate component * refactor: rename NewConversationMenu to EndpointMenu * style: mobile and desktop optimizations * chore: eslint changes * chore(eslintrc.js): update eslint configuration to use 'prettier' plugin chore(postcss.config.cjs): update postcss configuration to use single quotes for require statements fix(helpers.js): fix fs.rmSync function call to delete node_modules directory recursively feat(update.js): add support for skipping git commands with '-g' flag * chore(ModelSelect.tsx): add support for azureOpenAI option component chore(Settings.tsx): add support for azureOpenAI option component chore(package.json): add rebuild:package-lock and update:branch scripts * fix(OptionHover.tsx): fix accessing nested properties in types object feat(OptionHover.tsx): add check for existence of text before rendering HoverCardContent * chore(style.css): update transition duration for options-bar from 0.3s to 0.25s * fix(ScrollToBottom.jsx): fix z-index value for scroll button * style: improve dialogs * fix(Nav.jsx): adjust width and max-width of nav component * chore(Nav.jsx): update max-width class for nav component in different screen sizes chore(Dialog.tsx): update class for DialogFooter component to use flex-row layout * fix(client): fix node_module resolution with path mapping * fix(AdjustToneButton.jsx): add z-index to adjust tone button for proper layering fix(TextChat.jsx): change onClick function to use arrow function to avoid immediate execution fix(mobile.css): update z-index for nav and nav-mask for proper layering chore(package.json): rename update:branch script to reinstall for clarity and consistency * fix(OptionsBar/Settings): add null checks for conversation in BingAI.tsx, ChatGPT.tsx, Plugins.tsx, Settings.tsx * style(TextChat/OptionsBar): match official site styles, setup regen/continue/stop buttons div * chore: Import and apply removeFocusOutlines utility across various components, and rename removeButtonOutline to removeFocusOutlines chore(Settings): Remove unused import and conditionally return null if conversation is falsy * feat(hooks): add useLocalize hook The useLocalize hook is added to the hooks/index.ts file. This hook allows for localization of phrases using the localize function from the ~/localization/Translation module. The hook uses the lang value from the store to determine the current language and returns a function that takes a phraseKey and optional values array as arguments and returns the localized phrase. * refactor(OptionHover.tsx): Update text keys for OptionHover component, use new hook: useLocalize * refactor(useDocumentTitle.ts): refactor to TS * fix(typescript): type issues and update typescript linting deps * refactor: Update ThemeContext and useOnClickOutside to TypeScript chore(useDidMountEffect.js): Remove useDidMountEffect hook * feat: GenerationButtons for stop/continue/regen, remove AdjustToneButton in favor of alternate advanced mode/Settings in OptionsBar * fix(EndpointOptionsPopover.tsx): change switchToSimpleMode function name to closePopover fix(GenerationButtons.tsx): change advancedMode prop name to showPopover fix(OptionsBar.tsx): change advancedMode state name to showPopover feat(OptionsBar.tsx): add logic to show/hide popover based on showPopover state fix(types.ts): change switchToSimpleMode function name to closePopover * chore: remove template button * chore(GenerationButtons.tsx): adjust positioning of the div element chore(Plugins.tsx): adjust width of the MultiSelectDropDown component chore(OptionsBar.tsx): adjust padding of the button element * refactor(EditPresetDialog): use new modular higher order components * chore(newoptionsbar.html): delete unused file newoptionsbar.html * refactor(EditPresetDialog): convert to TS * chore(babel.config.cjs): update babel configuration, linting * chore(EditPresetDialog.tsx): update className for DialogTemplate to include pb-0 chore(EndpointOptionsDialog.jsx): update className for DialogTemplate to include pb-0 chore(PopoverButtons.tsx): add buttonClass prop to PopoverButtons component chore(DialogTemplate.tsx): update className for the footer div to include h-auto chore(Dropdown.jsx): remove id prop from Dropdown component chore(mobile.css): update transition duration for .nav class from 0.2s to 0.15s * refactor(EditPresetDialog.tsx): simplify localization usage with hook * chore(EditPresetDialog.tsx): update containerClassName to include z-index value * fix(endpoints.ts): change type of endpointsConfig atom to TEndpointsConfig refactor(cleanupPreset.ts): convert to TS fix(index.ts): export cleanupPreset utility function fix(types.ts): add missing properties to TPreset type * refactor(EndpointOptionsDialog): convert to TS * fix(EditPresetDialog.tsx): - import cleanupPreset from index - add null check before submitting preset - add null check before exporting preset refactor(SaveAsPresetDialog.tsx): convert to TS fix(usePresetOptions.ts): import cleanupPreset from index fix(types.ts): - make title prop optional in EditPresetProps - change preset prop in CleanupPreset to be partial * chore: reorganize imports in App, EndpointMenu, Messages, and ExportModel components feat(ScreenshotContext.jsx): add ScreenshotContext to hooks/index chore(index.ts): export ThemeContext, ScreenshotContext, ApiErrorBoundaryContext hooks, cleanupPreset, and getIcon functions from utils * wip: add headerClassName for dialog template * chore(EndpointOptionsDialog.tsx): remove unused headerClassName prop chore(EndpointOptionsDialog.tsx): adjust height of main container in mobile and desktop view * fix(react-query-service.ts): change return type of useGetEndpointsQuery to QueryObserverResult<t.TEndpointsConfig> * refactor: imports from index and refactor to TS * refactor: refactor all svg components to TS * refactor: refactor all UI components to TS, remove unused component * fix(SelectDropDown.tsx): remove file extension from import statement for CheckMark component * fix: SaveAsPresetDialog typing issue * fix(OptionsBar): close popover when an endpoint with no settings is selected * chore(ChatGPT.tsx): update width of model select dropdown to 60px refactor(types.ts): decouple ModelSelectProps from SettingsProps * fix(popover Settings): space taken from the options menu for each endpoint * fix:'Set token first' element alignment, add padding to endpointmenu icon in mobile * style: match official site header * refactor(EndpointOptionsDialog): make functionality explicitly saving current convos as presets * fix(useLocalize.ts): change values parameter from an array to rest parameters * refactor(EndpointSettings): Utilize useLocalize hook for all endpoint settings * fix(Popover): correct spacing/center and remove focus outlines for close button * chore: employ use of cn (clsx) in Popover styles * chore(EditPresetDialog.tsx): update className to add padding bottom chore(EndpointOptionsDialog.tsx): update className to add padding bottom * style(EndpointMenu, TextChat): add better styling at diff. breakpoints * refactor(EndpointSettings): consolidate container style to higher order component * refactor(EditPresetDialog.tsx): pass custom style to Settings from here * style: setting dialogs improved in all views * style(EndpointMenu): improve UX for mobile * style(PresetDialog): increase height so scrollbar isn't triggered * chore(EditPresetDialog.tsx): update className to include xl height for DialogTemplate chore(InputNumber.tsx): update className to include max height for InputNumber component * fix: light mode styling * fix(OptionsBar/ScrollToBottom/Popover): quick fix to rework in future: hide scrollToBottom when Popover is open * style: remove bg-gradient around textarea in mobile view * chore(ThemeContext.tsx): refactor ThemeContext to use default context value, also fixes type issue * chore(EditPresetDialog.tsx): adjust grid layout in EditPresetDialog component * style(TextChat): make gradient more opaque/smoother * fix(TextChat.jsx): fix background gradient color based on theme and system preference * test(layout-test-utils.tsx): add mock implementation for window.matchMedia in test setup feat(layout-test-utils.tsx): add authConfig prop to AuthContextProvider in renderWithProvidersWrapper function chore(tsconfig.json): include test directory in tsconfig include section * chore(jest.config.cjs): update test file paths in jest configuration chore(Login.spec.tsx): update test file path in import statement chore(LoginForm.spec.tsx): update test file path in import statement chore(Registration.spec.tsx): update test file path in import statement chore(PluginAuthForm.spec.tsx): update test file path in import statement chore(PluginStoreDialog.spec.tsx): update test file path in import statement chore(layout-test-utils.tsx): move matchMedia mock to separate file chore(tsconfig.json): add path mapping for test files in client directory * test: add import for 'test/matchMedia.mock' in test files The changes in this commit add an import statement for 'test/matchMedia.mock' in multiple test files. This import is necessary for mocking the behavior of the matchMedia function during testing. * style(ClearConvosDialog): remove borders from button and modal, uniform button size * fix(AgentSettings.tsx): overlapping issue * fix(PresetDialogs): improve spacing of top row and dialog content * style(Settings): 2nd column will now dynamically adjust better across all screen sizes * style(ModelSelect): improve styling for mobile/desktop, add hover shadow feat(ModelSelect/Plugins): hide ModelSelect when screen is small * refactor(RowButton, buildTree): convert to TS * style(ModelSelect): add transition effect to shadows on hover
This commit is contained in:
parent
fb99e5a7da
commit
956aa6c674
203 changed files with 5062 additions and 4327 deletions
|
|
@ -8,7 +8,7 @@ import { cn } from '../../utils';
|
|||
const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className = '', ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@ import * as React from 'react';
|
|||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { Button } from '../ui/Button';
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
import { cn } from '../../utils';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
const DialogPortal = ({ className, children, ...props }: DialogPrimitive.DialogPortalProps) => (
|
||||
const DialogPortal = ({
|
||||
className = '',
|
||||
children,
|
||||
...props
|
||||
}: DialogPrimitive.DialogPortalProps) => (
|
||||
<DialogPrimitive.Portal className={cn(className)} {...props}>
|
||||
<div className="fixed inset-0 z-[999] flex items-start justify-center sm:items-center">
|
||||
{children}
|
||||
|
|
@ -24,8 +27,8 @@ const DialogOverlay = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
className={cn(
|
||||
'data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out fixed inset-0 z-[999] bg-gray-500/90 transition-all duration-100 dark:bg-gray-800/90',
|
||||
className,
|
||||
'fixed inset-0 z-[999] bg-gray-500/90 transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in dark:bg-gray-800/90',
|
||||
className ?? '',
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
|
|
@ -42,14 +45,14 @@ const DialogContent = React.forwardRef<
|
|||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-[999] grid w-full gap-4 overflow-y-auto rounded-b-lg bg-white pb-6 sm:rounded-lg md:w-[680px]',
|
||||
'fixed z-[999] grid w-full gap-4 rounded-b-lg bg-white pb-6 animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0',
|
||||
'dark:bg-slate-900',
|
||||
className,
|
||||
className ?? '',
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-[1.88rem] rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 dark:data-[state=open]:bg-slate-800">
|
||||
<DialogPrimitive.Close className="absolute right-4 top-[.80rem] rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 dark:data-[state=open]:bg-slate-800 sm:top-[1.88rem]">
|
||||
<X className="h-4 w-4 text-black dark:text-white" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
|
|
@ -61,8 +64,8 @@ DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|||
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col space-y-2 border-b border-black/10 p-6 text-center dark:border-white/10 sm:text-left',
|
||||
className,
|
||||
'flex flex-col space-y-2 border-b border-black/10 p-2 text-center dark:border-white/10 sm:p-6 sm:text-left',
|
||||
className ?? '',
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
|
@ -73,7 +76,7 @@ const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivEleme
|
|||
<div
|
||||
className={cn(
|
||||
'flex flex-col-reverse px-6 sm:flex-row sm:justify-between sm:space-x-2',
|
||||
className,
|
||||
className ?? '',
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
|
@ -86,7 +89,7 @@ const DialogTitle = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn('text-lg font-semibold text-slate-900', 'dark:text-slate-50', className)}
|
||||
className={cn('text-lg font-semibold text-slate-900', 'dark:text-slate-50', className ?? '')}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
|
@ -98,7 +101,7 @@ const DialogDescription = React.forwardRef<
|
|||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn('text-sm text-slate-500', 'dark:text-slate-400', className)}
|
||||
className={cn('text-sm text-slate-500', 'dark:text-slate-400', className ?? '')}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
|
@ -112,7 +115,7 @@ const DialogClose = React.forwardRef<
|
|||
ref={ref}
|
||||
className={cn(
|
||||
'mt-2 inline-flex h-10 items-center justify-center rounded-md border border-slate-200 bg-transparent px-4 py-2 text-sm font-semibold text-slate-900 transition-colors hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-gray-900 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 sm:mt-0',
|
||||
className,
|
||||
className ?? '',
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
|
@ -128,7 +131,7 @@ const DialogButton = React.forwardRef<
|
|||
variant="outline"
|
||||
className={cn(
|
||||
'mt-2 inline-flex h-10 items-center justify-center rounded-md border border-slate-200 bg-transparent px-4 py-2 text-sm font-semibold text-slate-900 transition-colors hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-gray-900 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 sm:mt-0',
|
||||
className,
|
||||
className ?? '',
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'test/matchMedia.mock';
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
|
|
@ -13,7 +14,12 @@ describe('DialogTemplate', () => {
|
|||
|
||||
it('renders correctly with all props', () => {
|
||||
const { getByText } = render(
|
||||
<Dialog open onOpenChange={() => {}}>
|
||||
<Dialog
|
||||
open
|
||||
onOpenChange={() => {
|
||||
return;
|
||||
}}
|
||||
>
|
||||
<DialogTemplate
|
||||
title="Test Dialog"
|
||||
description="Test Description"
|
||||
|
|
@ -36,7 +42,12 @@ describe('DialogTemplate', () => {
|
|||
|
||||
it('renders correctly without optional props', () => {
|
||||
const { getByText, queryByText } = render(
|
||||
<Dialog open onOpenChange={() => {}}>
|
||||
<Dialog
|
||||
open
|
||||
onOpenChange={() => {
|
||||
return;
|
||||
}}
|
||||
>
|
||||
<DialogTemplate title="Test Dialog" />
|
||||
</Dialog>,
|
||||
);
|
||||
|
|
@ -52,7 +63,12 @@ describe('DialogTemplate', () => {
|
|||
|
||||
it('calls selectHandler when the select button is clicked', () => {
|
||||
const { getByText } = render(
|
||||
<Dialog open onOpenChange={() => {}}>
|
||||
<Dialog
|
||||
open
|
||||
onOpenChange={() => {
|
||||
return;
|
||||
}}
|
||||
>
|
||||
<DialogTemplate
|
||||
title="Test Dialog"
|
||||
selection={{ selectHandler: mockSelectHandler, selectText: 'Select' }}
|
||||
|
|
|
|||
|
|
@ -23,17 +23,19 @@ type DialogTemplateProps = {
|
|||
leftButtons?: ReactNode;
|
||||
selection?: SelectionProps;
|
||||
className?: string;
|
||||
headerClassName?: string;
|
||||
};
|
||||
|
||||
const DialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivElement>) => {
|
||||
const { title, description, main, buttons, leftButtons, selection, className } = props;
|
||||
const { title, description, main, buttons, leftButtons, selection, className, headerClassName } =
|
||||
props;
|
||||
const { selectHandler, selectClasses, selectText } = selection || {};
|
||||
|
||||
const defaultSelect =
|
||||
'bg-gray-900 text-white transition-colors hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-200 dark:focus:ring-gray-400 dark:focus:ring-offset-gray-900';
|
||||
return (
|
||||
<DialogContent ref={ref} className={cn('shadow-2xl dark:bg-gray-900', className || '')}>
|
||||
<DialogHeader>
|
||||
<DialogHeader className={cn('sm:pb-2', headerClassName ?? '')}>
|
||||
<DialogTitle className="text-lg font-medium leading-6 text-gray-900 dark:text-gray-200">
|
||||
{title}
|
||||
</DialogTitle>
|
||||
|
|
@ -46,7 +48,7 @@ const DialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivE
|
|||
<div className="px-6">{main ? main : null}</div>
|
||||
<DialogFooter>
|
||||
<div>{leftButtons ? leftButtons : null}</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex h-auto gap-2">
|
||||
<DialogClose className="dark:hover:gray-400 border-gray-700">Cancel</DialogClose>
|
||||
{buttons ? buttons : null}
|
||||
{selection ? (
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ import * as React from 'react';
|
|||
|
||||
import { cn } from '../../utils';
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
className={cn(
|
||||
'flex h-10 w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900',
|
||||
className,
|
||||
className ?? '',
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ import * as React from 'react';
|
|||
|
||||
import RCInputNumber from 'rc-input-number';
|
||||
import * as InputNumberPrimitive from 'rc-input-number';
|
||||
|
||||
import { cn } from '../../utils/index.jsx';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
// TODO help needed
|
||||
// React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
|
|
@ -20,8 +19,8 @@ const InputNumber = React.forwardRef<
|
|||
return (
|
||||
<RCInputNumber
|
||||
className={cn(
|
||||
'flex h-10 w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900',
|
||||
className,
|
||||
'flex max-h-5 w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900',
|
||||
className ?? '',
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Button } from './Button.tsx';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuRadioItem,
|
||||
} from './DropdownMenu.tsx';
|
||||
import store from '~/store';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { localize } from '~/localization/Translation';
|
||||
|
||||
const ModelSelect = ({ model, onChange, availableModels, ...props }) => {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const lang = useRecoilValue(store.lang);
|
||||
|
||||
return (
|
||||
<DropdownMenu open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button {...props}>
|
||||
<span className="w-full text-center text-xs font-medium font-normal">
|
||||
{localize(lang, 'com_ui_model')}: {model}
|
||||
</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-56 dark:bg-gray-700"
|
||||
onCloseAutoFocus={(event) => event.preventDefault()}
|
||||
>
|
||||
<DropdownMenuLabel className="dark:text-gray-300">
|
||||
{localize(lang, 'com_ui_select_model')}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuRadioGroup value={model} onValueChange={onChange} className="overflow-y-auto">
|
||||
{availableModels.map((model) => (
|
||||
<DropdownMenuRadioItem
|
||||
key={model}
|
||||
value={model}
|
||||
className="dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
|
||||
>
|
||||
{model}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelSelect;
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import React, { useState, useRef } from 'react';
|
||||
import CheckMark from '../svg/CheckMark.jsx';
|
||||
import useOnClickOutside from '~/hooks/useOnClickOutside.js';
|
||||
import { Listbox, Transition } from '@headlessui/react';
|
||||
import { Wrench, ArrowRight } from 'lucide-react';
|
||||
import { CheckMark } from '~/components/svg';
|
||||
import useOnClickOutside from '~/hooks/useOnClickOutside';
|
||||
import { MultiSelectDropDownProps } from 'librechat-data-provider';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
function MultiSelectDropDown({
|
||||
|
|
@ -17,31 +18,37 @@ function MultiSelectDropDown({
|
|||
isSelected,
|
||||
className,
|
||||
optionValueKey = 'value',
|
||||
}) {
|
||||
}: MultiSelectDropDownProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const menuRef = useRef(null);
|
||||
const excludeIds = ['select-plugin', 'plugins-label', 'selected-plugins'];
|
||||
useOnClickOutside(menuRef, () => setIsOpen(false), excludeIds);
|
||||
|
||||
const handleSelect = (option) => {
|
||||
const handleSelect: (value: string) => void = (option) => {
|
||||
setSelected(option);
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const transitionProps = { className: 'top-full mt-3' };
|
||||
if (showAbove) {
|
||||
transitionProps.className = 'bottom-full mb-3';
|
||||
}
|
||||
const openProps = { open: isOpen };
|
||||
return (
|
||||
<div className={cn('flex items-center justify-center gap-2', containerClassName)}>
|
||||
<div className={cn('flex items-center justify-center gap-2', containerClassName ?? '')}>
|
||||
<div className="relative w-full">
|
||||
{/* the function typing is correct but there's still an issue here */}
|
||||
<Listbox value={value} onChange={handleSelect} disabled={disabled}>
|
||||
{() => (
|
||||
<>
|
||||
<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',
|
||||
className,
|
||||
className ?? '',
|
||||
)}
|
||||
id={excludeIds[0]}
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
open={isOpen}
|
||||
{...openProps}
|
||||
>
|
||||
{' '}
|
||||
{showLabel && (
|
||||
|
|
@ -111,13 +118,13 @@ function MultiSelectDropDown({
|
|||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
className={showAbove ? 'bottom-full mb-3' : 'top-full mt-3'}
|
||||
{...transitionProps}
|
||||
>
|
||||
<Listbox.Options
|
||||
ref={menuRef}
|
||||
className="absolute z-50 mt-2 max-h-60 w-full overflow-auto rounded bg-white text-base text-xs ring-1 ring-black/10 focus:outline-none dark:bg-gray-800 dark:ring-white/20 dark:last:border-0 md:w-[100%]"
|
||||
>
|
||||
{availableValues.map((option, i) => {
|
||||
{availableValues.map((option, i: number) => {
|
||||
if (!option) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import store from '~/store';
|
||||
import { localize } from '~/localization/Translation';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export default function Prompt({ title, prompt }) {
|
||||
const lang = useRecoilValue(store.lang);
|
||||
export default function Prompt({ title, prompt }: { title: string; prompt: string }) {
|
||||
const localize = useLocalize();
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -18,7 +16,7 @@ export default function Prompt({ title, prompt }) {
|
|||
{prompt}
|
||||
</p>
|
||||
</button>
|
||||
<span className="font-medium">{localize(lang, 'com_ui_use_prompt')} →</span>
|
||||
<span className="font-medium">{localize('com_ui_use_prompt')} →</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,22 @@
|
|||
import React from 'react';
|
||||
import CheckMark from '../svg/CheckMark.jsx';
|
||||
import CheckMark from '../svg/CheckMark';
|
||||
import { Listbox, Transition } from '@headlessui/react';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
type SelectDropDownProps = {
|
||||
id?: string;
|
||||
title?: string;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
setValue: (value: string) => void;
|
||||
availableValues: string[];
|
||||
showAbove?: boolean;
|
||||
showLabel?: boolean;
|
||||
containerClassName?: string;
|
||||
subContainerClassName?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function SelectDropDown({
|
||||
title = 'Model',
|
||||
value,
|
||||
|
|
@ -14,17 +28,21 @@ function SelectDropDown({
|
|||
containerClassName,
|
||||
subContainerClassName,
|
||||
className,
|
||||
}) {
|
||||
}: SelectDropDownProps) {
|
||||
const transitionProps = { className: 'top-full mt-3' };
|
||||
if (showAbove) {
|
||||
transitionProps.className = 'bottom-full mb-3';
|
||||
}
|
||||
return (
|
||||
<div className={cn('flex items-center justify-center gap-2', containerClassName)}>
|
||||
<div className={cn('relative w-full', subContainerClassName)}>
|
||||
<div className={cn('flex items-center justify-center gap-2', containerClassName ?? '')}>
|
||||
<div className={cn('relative w-full', subContainerClassName ?? '')}>
|
||||
<Listbox value={value} onChange={setValue} disabled={disabled}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<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',
|
||||
className,
|
||||
className ?? '',
|
||||
)}
|
||||
>
|
||||
{' '}
|
||||
|
|
@ -74,10 +92,10 @@ function SelectDropDown({
|
|||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
className={showAbove ? 'bottom-full mb-3' : 'top-full mt-3'}
|
||||
{...transitionProps}
|
||||
>
|
||||
<Listbox.Options className="absolute z-10 mt-2 max-h-60 w-full overflow-auto rounded bg-white text-base text-xs ring-1 ring-black/10 focus:outline-none dark:bg-gray-800 dark:ring-white/20 dark:last:border-0 md:w-[100%]">
|
||||
{availableValues.map((option, i) => (
|
||||
{availableValues.map((option: string, i: number) => (
|
||||
<Listbox.Option
|
||||
key={i}
|
||||
value={option}
|
||||
|
|
@ -15,14 +15,19 @@ const Slider = React.forwardRef<React.ElementRef<typeof SliderPrimitive.Root>, S
|
|||
({ className, doubleClickHandler, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn('relative flex w-full touch-none select-none items-center', className)}
|
||||
className={cn('relative flex w-full touch-none select-none items-center', className ?? '')}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-1 w-full grow overflow-hidden rounded-full bg-gray-100 dark:bg-gray-900">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-gray-400 dark:bg-gray-400" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb
|
||||
onClick={useDoubleClick(doubleClickHandler) ?? (() => {})}
|
||||
onClick={
|
||||
useDoubleClick(doubleClickHandler) ??
|
||||
(() => {
|
||||
return;
|
||||
})
|
||||
}
|
||||
className="block h-4 w-4 rounded-full border-2 border-gray-400 bg-white transition-colors focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:border-gray-100 dark:bg-gray-400 dark:focus:ring-gray-400 dark:focus:ring-offset-gray-900"
|
||||
/>
|
||||
</SliderPrimitive.Root>
|
||||
|
|
|
|||
|
|
@ -1,27 +1,25 @@
|
|||
import ChatIcon from '../svg/ChatIcon';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import store from '~/store';
|
||||
import { localize } from '~/localization/Translation';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export default function Templates({ showTemplates }) {
|
||||
const lang = useRecoilValue(store.lang);
|
||||
export default function Templates({ showTemplates }: { showTemplates: () => void }) {
|
||||
const localize = useLocalize();
|
||||
|
||||
return (
|
||||
<div id="templates-wrapper" className="mt-6 flex items-start gap-3.5 text-center ">
|
||||
<div className="flex flex-1 flex-col gap-3.5">
|
||||
<ChatIcon />
|
||||
<h2 className="text-lg font-normal">{localize(lang, 'com_ui_prompt_templates')}</h2>
|
||||
<h2 className="text-lg font-normal">{localize('com_ui_prompt_templates')}</h2>
|
||||
<ul className="flex flex-col gap-3.5">
|
||||
<ul className="flex flex-col gap-3.5"></ul>
|
||||
|
||||
<div className="flex flex-1 flex-col items-center gap-3.5">
|
||||
<span className="text-sm text-gray-700 dark:text-gray-400">
|
||||
{localize(lang, 'com_ui_showing')}{' '}
|
||||
{localize('com_ui_showing')}{' '}
|
||||
<span className="font-semibold text-gray-900 dark:text-white">1</span>{' '}
|
||||
{localize(lang, 'com_ui_of')}{' '}
|
||||
{localize('com_ui_of')}{' '}
|
||||
<a id="prompt-link">
|
||||
<span className="font-semibold text-gray-900 dark:text-white">
|
||||
1 {localize(lang, 'com_ui_entries')}
|
||||
1 {localize('com_ui_entries')}
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
|
|
@ -30,21 +28,21 @@ export default function Templates({ showTemplates }) {
|
|||
className="btn btn-neutral justify-center gap-2 border-0 md:border"
|
||||
>
|
||||
<ChatIcon />
|
||||
{localize(lang, 'com_ui_hide_prompt_templates')}
|
||||
{localize('com_ui_hide_prompt_templates')}
|
||||
</button>
|
||||
<div
|
||||
// onclick="selectPromptTemplate(0)"
|
||||
className="flex w-full flex-col gap-2 rounded-md bg-gray-50 p-4 text-left hover:bg-gray-200 dark:bg-white/5 "
|
||||
>
|
||||
<h2 className="m-auto flex items-center gap-3 text-lg font-normal md:flex-col md:gap-2">
|
||||
{localize(lang, 'com_ui_dan')}
|
||||
{localize('com_ui_dan')}
|
||||
</h2>
|
||||
<button>
|
||||
<p className="w-full rounded-md bg-gray-50 p-3 hover:bg-gray-200 dark:bg-white/5 dark:hover:bg-gray-900">
|
||||
{localize(lang, 'com_ui_dan_template')}
|
||||
{localize('com_ui_dan_template')}
|
||||
</p>
|
||||
</button>
|
||||
<span className="font-medium">{localize(lang, 'com_ui_use_prompt')} →</span>
|
||||
<span className="font-medium">{localize('com_ui_use_prompt')} →</span>
|
||||
</div>
|
||||
<div className="xs:mt-0 mt-2 inline-flex">
|
||||
<button
|
||||
|
|
@ -52,14 +50,14 @@ export default function Templates({ showTemplates }) {
|
|||
className="bg-gray-100 px-4 py-2 font-medium hover:bg-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:hover:text-white"
|
||||
style={{ borderRadius: '6px 0 0 6px' }}
|
||||
>
|
||||
{localize(lang, 'com_ui_prev')}
|
||||
{localize('com_ui_prev')}
|
||||
</button>
|
||||
<button
|
||||
// onclick="nextPromptTemplatesPage()"
|
||||
className="border-0 border-l border-gray-500 bg-gray-100 px-4 py-2 font-medium hover:bg-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:hover:text-white"
|
||||
style={{ borderRadius: '6px 0 0 6px' }}
|
||||
>
|
||||
{localize(lang, 'com_ui_next')}
|
||||
{localize('com_ui_next')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -8,7 +8,6 @@ export * from './Input';
|
|||
export * from './InputNumber';
|
||||
export * from './Label';
|
||||
export * from './Landing';
|
||||
export * from './ModelSelect';
|
||||
export * from './Prompt';
|
||||
export * from './Slider';
|
||||
export * from './Switch';
|
||||
|
|
@ -17,5 +16,4 @@ export * from './Templates';
|
|||
export * from './Textarea';
|
||||
export { default as Dropdown } from './Dropdown';
|
||||
export { default as SelectDropDown } from './SelectDropDown';
|
||||
export { default as DialogTemplate } from './DialogTemplate';
|
||||
export { default as MultiSelectDropDown } from './MultiSelectDropDown';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue