Compare commits
8 Commits
main
...
feat/compo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f75010369c | ||
|
|
dc9d219f3e | ||
|
|
b0c5db6756 | ||
|
|
ff722366e9 | ||
|
|
4f06c159be | ||
|
|
e042e1500f | ||
|
|
0503f0f903 | ||
|
|
e4adfe771b |
@@ -109,6 +109,9 @@
|
||||
"tailwindcss-radix": "^2.8.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jotai": "^2.12.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-runtime": "^7.22.15",
|
||||
"@babel/preset-env": "^7.22.15",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type RenderProp<
|
||||
P = React.HTMLAttributes<any> & {
|
||||
ref?: React.Ref<any>;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { Input, Label, OGDialog, Button } from '~/components/ui';
|
||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||
import { Button, Input, Label, OGDialog, OGDialogTemplate } from '~/components';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export interface ConfigFieldDetail {
|
||||
@@ -3,7 +3,7 @@ import { SettingsIcon } from 'lucide-react';
|
||||
import { Constants } from 'librechat-data-provider';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import type { TUpdateUserPlugins, TPlugin } from 'librechat-data-provider';
|
||||
import MCPConfigDialog, { type ConfigFieldDetail } from '~/components/ui/MCPConfigDialog';
|
||||
import MCPConfigDialog, { type ConfigFieldDetail } from './MCPConfigDialog';
|
||||
import { useToastContext, useBadgeRowContext } from '~/Providers';
|
||||
import MultiSelect from '~/components/ui/MultiSelect';
|
||||
import { MCPIcon } from '~/components/svg';
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
InputNumber,
|
||||
SelectDropDown,
|
||||
HoverCardTrigger,
|
||||
MultiSelectDropDown,
|
||||
} from '~/components/ui';
|
||||
import {
|
||||
removeFocusOutlines,
|
||||
@@ -25,6 +24,7 @@ import {
|
||||
} from '~/utils';
|
||||
import OptionHoverAlt from '~/components/SidePanel/Parameters/OptionHover';
|
||||
import { useLocalize, useDebouncedInput } from '~/hooks';
|
||||
import MultiSelectDropDown from '~/components/Input/ModelSelect/MultiSelectDropDown';
|
||||
import OptionHover from './OptionHover';
|
||||
import { ESide } from '~/common';
|
||||
import store from '~/store';
|
||||
@@ -165,7 +165,7 @@ export default function Settings({
|
||||
)}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2 ',
|
||||
'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Wrench, ArrowRight } from 'lucide-react';
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
@@ -7,12 +8,11 @@ import {
|
||||
ListboxOption,
|
||||
Transition,
|
||||
} from '@headlessui/react';
|
||||
import { Wrench, ArrowRight } from 'lucide-react';
|
||||
import { CheckMark } from '~/components/svg';
|
||||
import useOnClickOutside from '~/hooks/useOnClickOutside';
|
||||
import { useMultiSearch } from './MultiSearch';
|
||||
import { cn } from '~/utils/';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import { useMultiSearch } from '~/components/MultiSearch';
|
||||
import { useOnClickOutside } from '~/hooks';
|
||||
import { CheckMark } from '~/svgs';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
export type TMultiSelectDropDownProps = {
|
||||
title?: string;
|
||||
@@ -142,7 +142,7 @@ function MultiSelectDropDown({
|
||||
viewBox="0 0 24 24"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -2,7 +2,7 @@ import { Wrench } from 'lucide-react';
|
||||
import { Root, Trigger, Content, Portal } from '@radix-ui/react-popover';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import MenuItem from '~/components/Chat/Menus/UI/MenuItem';
|
||||
import { useMultiSearch } from './MultiSearch';
|
||||
import { useMultiSearch } from '~/components';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
type SelectDropDownProps = {
|
||||
@@ -32,8 +32,6 @@ function MultiSelectPop({
|
||||
optionValueKey = 'value',
|
||||
searchPlaceholder,
|
||||
}: SelectDropDownProps) {
|
||||
// const localize = useLocalize();
|
||||
|
||||
const title = _title;
|
||||
const excludeIds = ['select-plugin', 'plugins-label', 'selected-plugins'];
|
||||
|
||||
@@ -54,14 +52,14 @@ function MultiSelectPop({
|
||||
<button
|
||||
data-testid="select-dropdown-button"
|
||||
className={cn(
|
||||
'relative flex 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-gray-700 dark:bg-gray-800 dark:bg-gray-800 sm:text-sm',
|
||||
'relative flex 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-gray-700 dark:bg-gray-800 sm:text-sm',
|
||||
'pointer-cursor font-normal',
|
||||
'hover:bg-gray-50 radix-state-open:bg-gray-50 dark:hover:bg-gray-700 dark:radix-state-open:bg-gray-700',
|
||||
)}
|
||||
>
|
||||
{' '}
|
||||
{showLabel && (
|
||||
<label className="block text-xs text-gray-700 dark:text-gray-500 ">{title}</label>
|
||||
<label className="block text-xs text-gray-700 dark:text-gray-500">{title}</label>
|
||||
)}
|
||||
<span className="inline-flex" id={excludeIds[2]}>
|
||||
<span
|
||||
@@ -73,7 +71,7 @@ function MultiSelectPop({
|
||||
{/* {!showLabel && title.length > 0 && (
|
||||
<span className="text-xs text-gray-700 dark:text-gray-500">{title}:</span>
|
||||
)} */}
|
||||
<span className="flex items-center gap-1 ">
|
||||
<span className="flex items-center gap-1">
|
||||
<div className="flex gap-1">
|
||||
{value.length === 0 && 'None selected'}
|
||||
{value.map((v, i) => (
|
||||
@@ -98,7 +96,7 @@ function MultiSelectPop({
|
||||
viewBox="0 0 24 24"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -4,15 +4,10 @@ import { useState, useEffect, useMemo } from 'react';
|
||||
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import {
|
||||
Button,
|
||||
MultiSelectPop,
|
||||
SelectDropDown,
|
||||
SelectDropDownPop,
|
||||
MultiSelectDropDown,
|
||||
} from '~/components/ui';
|
||||
import { Button, SelectDropDown, SelectDropDownPop, MultiSelectDropDown } from '~/components/ui';
|
||||
import { useSetIndexOptions, useAuthContext, useMediaQuery, useLocalize } from '~/hooks';
|
||||
import { cn, cardStyle, selectPlugins, processPlugins } from '~/utils';
|
||||
import MultiSelectPop from './MultiSelectPop';
|
||||
import store from '~/store';
|
||||
|
||||
export default function PluginsByIndex({
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Root, Trigger, Content, Portal } from '@radix-ui/react-popover';
|
||||
import MenuItem from '~/components/Chat/Menus/UI/MenuItem';
|
||||
import { useMultiSearch } from '~/components';
|
||||
import type { Option } from '~/common';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils/';
|
||||
import { useMultiSearch } from './MultiSearch';
|
||||
|
||||
type SelectDropDownProps = {
|
||||
id?: string;
|
||||
@@ -1,26 +1,28 @@
|
||||
import { useCallback, useState, useMemo, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { TrashIcon, MessageSquare, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import type { SharedLinkItem, SharedLinksListParams } from 'librechat-data-provider';
|
||||
import {
|
||||
OGDialog,
|
||||
OGDialogTemplate,
|
||||
OGDialogTrigger,
|
||||
OGDialogContent,
|
||||
OGDialogHeader,
|
||||
OGDialogTitle,
|
||||
TooltipAnchor,
|
||||
DataTable,
|
||||
Spinner,
|
||||
Button,
|
||||
Label,
|
||||
Spinner,
|
||||
} from '~/components';
|
||||
import { useDeleteSharedLinkMutation, useSharedLinksQuery } from '~/data-provider';
|
||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||
import { useLocalize, useMediaQuery } from '~/hooks';
|
||||
import DataTable from '~/components/ui/DataTable';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { formatDate } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
@@ -36,6 +38,7 @@ export default function SharedLinks() {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||
const isSearchEnabled = useRecoilValue(store.search);
|
||||
const [queryParams, setQueryParams] = useState<SharedLinksListParams>(DEFAULT_PARAMS);
|
||||
const [deleteRow, setDeleteRow] = useState<SharedLinkItem | null>(null);
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
@@ -308,6 +311,7 @@ export default function SharedLinks() {
|
||||
onFilterChange={debouncedFilterChange}
|
||||
filterValue={queryParams.search}
|
||||
isLoading={isLoading}
|
||||
enableSearch={isSearchEnabled}
|
||||
/>
|
||||
</OGDialogContent>
|
||||
</OGDialog>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useMemo, useEffect } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { TrashIcon, ArchiveRestore, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
|
||||
import type { ConversationListParams, TConversation } from 'librechat-data-provider';
|
||||
import {
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
Label,
|
||||
TooltipAnchor,
|
||||
Spinner,
|
||||
DataTable,
|
||||
} from '~/components';
|
||||
import {
|
||||
useArchiveConvoMutation,
|
||||
@@ -19,10 +21,10 @@ import {
|
||||
} from '~/data-provider';
|
||||
import { useLocalize, useMediaQuery } from '~/hooks';
|
||||
import { MinimalIcon } from '~/components/Endpoints';
|
||||
import DataTable from '~/components/ui/DataTable';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { formatDate } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
const DEFAULT_PARAMS: ConversationListParams = {
|
||||
isArchived: true,
|
||||
@@ -39,7 +41,7 @@ export default function ArchivedChatsTable({
|
||||
const localize = useLocalize();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||
const { showToast } = useToastContext();
|
||||
|
||||
const isSearchEnabled = useRecoilValue(store.search);
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
const [queryParams, setQueryParams] = useState<ConversationListParams>(DEFAULT_PARAMS);
|
||||
const [deleteConversation, setDeleteConversation] = useState<TConversation | null>(null);
|
||||
@@ -272,6 +274,7 @@ export default function ArchivedChatsTable({
|
||||
isFetchingNextPage={isFetchingNextPage}
|
||||
isLoading={isLoading}
|
||||
showCheckboxes={false}
|
||||
enableSearch={isSearchEnabled}
|
||||
/>
|
||||
|
||||
<OGDialog open={isDeleteOpen} onOpenChange={onOpenChange}>
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Search } from 'lucide-react';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AnimatedSearchInput = ({
|
||||
value,
|
||||
onChange,
|
||||
isSearching: searching,
|
||||
placeholder,
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
isSearching?: boolean;
|
||||
placeholder: string;
|
||||
}) => {
|
||||
const isSearching = searching === true;
|
||||
const hasValue = value != null && value.length > 0;
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="relative rounded-lg transition-all duration-500 ease-in-out">
|
||||
<div className="relative">
|
||||
{/* Icon on the left */}
|
||||
<div className="absolute left-3 top-1/2 z-50 -translate-y-1/2">
|
||||
<Search
|
||||
className={cn(
|
||||
`
|
||||
h-4 w-4 transition-all duration-500 ease-in-out`,
|
||||
isSearching && hasValue ? 'text-blue-400' : 'text-gray-400',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Input field */}
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
className={`
|
||||
peer relative z-20 w-full rounded-lg bg-surface-secondary px-10
|
||||
py-2 outline-none ring-0 backdrop-blur-sm transition-all
|
||||
duration-500 ease-in-out placeholder:text-gray-400
|
||||
focus:outline-none focus:ring-0
|
||||
`}
|
||||
/>
|
||||
|
||||
{/* Gradient overlay */}
|
||||
<div
|
||||
className={`
|
||||
pointer-events-none absolute inset-0 z-20 rounded-lg
|
||||
bg-gradient-to-r from-blue-500/20 via-purple-500/20 to-blue-500/20
|
||||
transition-all duration-500 ease-in-out
|
||||
${isSearching && hasValue ? 'opacity-100 blur-sm' : 'opacity-0 blur-none'}
|
||||
`}
|
||||
/>
|
||||
|
||||
{/* Animated loading indicator */}
|
||||
<div
|
||||
className={`
|
||||
absolute right-3 top-1/2 z-20 -translate-y-1/2
|
||||
transition-all duration-500 ease-in-out
|
||||
${isSearching && hasValue ? 'scale-100 opacity-100' : 'scale-0 opacity-0'}
|
||||
`}
|
||||
>
|
||||
<div className="relative h-2 w-2">
|
||||
<div className="absolute inset-0 animate-ping rounded-full bg-blue-500/60" />
|
||||
<div className="absolute inset-0 rounded-full bg-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Outer glow effect */}
|
||||
<div
|
||||
className={`
|
||||
absolute -inset-8 -z-10
|
||||
transition-all duration-700 ease-in-out
|
||||
${isSearching && hasValue ? 'scale-105 opacity-100' : 'scale-100 opacity-0'}
|
||||
`}
|
||||
>
|
||||
<div className="absolute inset-0">
|
||||
<div
|
||||
className={`
|
||||
bg-gradient-radial absolute inset-0 from-blue-500/10 to-transparent
|
||||
transition-opacity duration-700 ease-in-out
|
||||
${isSearching && hasValue ? 'animate-pulse-slow opacity-100' : 'opacity-0'}
|
||||
`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
absolute inset-0 bg-gradient-to-r from-purple-500/5 via-blue-500/5 to-purple-500/5
|
||||
blur-xl transition-all duration-700 ease-in-out
|
||||
${isSearching && hasValue ? 'animate-gradient-x opacity-100' : 'opacity-0'}
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
absolute inset-0 -z-20 scale-100 bg-gradient-to-r from-blue-500/10
|
||||
via-purple-500/10 to-blue-500/10 opacity-0 blur-xl
|
||||
transition-all duration-500 ease-in-out
|
||||
peer-focus:scale-105 peer-focus:opacity-100
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnimatedSearchInput;
|
||||
@@ -1,5 +0,0 @@
|
||||
import { useDelayedRender } from '~/hooks';
|
||||
|
||||
const DelayedRender = ({ delay, children }) => useDelayedRender(delay)(() => children);
|
||||
|
||||
export default DelayedRender;
|
||||
@@ -1,26 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import * as SliderPrimitive from '@radix-ui/react-slider';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & { onDoubleClick?: () => void }
|
||||
>(({ className, onDoubleClick, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex w-full cursor-pointer touch-none select-none items-center',
|
||||
className,
|
||||
)}
|
||||
onDoubleClick={onDoubleClick}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { Slider };
|
||||
@@ -4,7 +4,7 @@ import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { useAcceptTermsMutation } from '~/data-provider';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { OGDialog } from '~/components/ui';
|
||||
import { OGDialog } from '~/components';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const TermsAndConditionsModal = ({
|
||||
@@ -73,7 +73,7 @@ const TermsAndConditionsModal = ({
|
||||
main={
|
||||
<section
|
||||
// Motivation: This is a dialog, so its content should be focusable
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
|
||||
tabIndex={0}
|
||||
className="max-h-[60vh] overflow-y-auto p-4"
|
||||
aria-label={localize('com_ui_terms_and_conditions')}
|
||||
|
||||
@@ -14,24 +14,19 @@ export * from './Prompts';
|
||||
export * from './Roles';
|
||||
export * from './SSE';
|
||||
export * from './AuthContext';
|
||||
export * from './ThemeContext';
|
||||
export * from './ScreenshotContext';
|
||||
export * from './ApiErrorBoundaryContext';
|
||||
export * from './Endpoint';
|
||||
|
||||
export type { TranslationKeys } from './useLocalize';
|
||||
|
||||
export { default as useToast } from './useToast';
|
||||
export { default as useTimeout } from './useTimeout';
|
||||
export { default as useNewConvo } from './useNewConvo';
|
||||
export { default as useLocalize } from './useLocalize';
|
||||
export { default as useMediaQuery } from './useMediaQuery';
|
||||
export { default as useChatBadges } from './useChatBadges';
|
||||
export { default as useScrollToRef } from './useScrollToRef';
|
||||
export { default as useLocalStorage } from './useLocalStorage';
|
||||
export { default as useDocumentTitle } from './useDocumentTitle';
|
||||
export { default as useDelayedRender } from './useDelayedRender';
|
||||
export { default as useOnClickOutside } from './useOnClickOutside';
|
||||
export { default as useSpeechToText } from './Input/useSpeechToText';
|
||||
export { default as useTextToSpeech } from './Input/useTextToSpeech';
|
||||
export { default as useGenerationsByLatest } from './useGenerationsByLatest';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// Translation.spec.ts
|
||||
|
||||
import i18n from './i18n';
|
||||
import English from './en/translation.json';
|
||||
import French from './fr/translation.json';
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
FileMapContext,
|
||||
SetConvoProvider,
|
||||
} from '~/Providers';
|
||||
import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal';
|
||||
import TermsAndConditionsModal from '~/components/ui'; // TODO: remove this comment after updating the import path of all components
|
||||
import { useUserTermsQuery, useGetStartupConfig } from '~/data-provider';
|
||||
import { Nav, MobileNav } from '~/components/Nav';
|
||||
import { useHealthCheck } from '~/data-provider';
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"test/setupTests.js",
|
||||
"env.d.ts",
|
||||
"../config/translations/**/*.ts"
|
||||
]
|
||||
, "../packages/client/src/hooks/useDelayedRender.tsx" ]
|
||||
}
|
||||
|
||||
487
package-lock.json
generated
487
package-lock.json
generated
@@ -2652,58 +2652,9 @@
|
||||
"vite-plugin-compression2": "^1.3.3",
|
||||
"vite-plugin-node-polyfills": "^0.23.0",
|
||||
"vite-plugin-pwa": "^0.21.2"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@ariakit/react": {
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.15.tgz",
|
||||
"integrity": "sha512-0V2LkNPFrGRT+SEIiObx/LQjR6v3rR+mKEDUu/3tq7jfCZ+7+6Q6EMR1rFaK+XMkaRY1RWUcj/rRDWAUWnsDww==",
|
||||
"dependencies": {
|
||||
"@ariakit/react-core": "0.4.15"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ariakit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@ariakit/react-core": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.17.tgz",
|
||||
"integrity": "sha512-kFF6n+gC/5CRQIyaMTFoBPio2xUe0k9rZhMNdUobWRmc/twfeLVkODx+8UVYaNyKilTge8G0JFqwvFKku/jKEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/core": "0.4.15",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@ariakit/react-core/node_modules/@ariakit/core": {
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.15.tgz",
|
||||
"integrity": "sha512-vvxmZvkNhiisKM+Y1TbGMUfVVchV/sWu9F0xw0RYADXcimWPK31dd9JnIZs/OQ5pwAryAHmERHwuGQVESkSjwQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"client/node_modules/@ariakit/react/node_modules/@ariakit/react-core": {
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.15.tgz",
|
||||
"integrity": "sha512-Up8+U97nAPJdyUh9E8BCEhJYTA+eVztWpHoo1R9zZfHd4cnBWAg5RHxEmMH+MamlvuRxBQA71hFKY/735fDg+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/core": "0.4.14",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
"jotai": "^2.12.5"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@babel/compat-data": {
|
||||
@@ -4574,6 +4525,27 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"client/node_modules/class-variance-authority": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.1.tgz",
|
||||
"integrity": "sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"clsx": "1.2.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://joebell.co.uk"
|
||||
}
|
||||
},
|
||||
"client/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"client/node_modules/core-js-compat": {
|
||||
"version": "3.40.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz",
|
||||
@@ -4605,6 +4577,33 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"client/node_modules/framer-motion": {
|
||||
"version": "11.18.2",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
|
||||
"integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^11.18.1",
|
||||
"motion-utils": "^11.18.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"client/node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
@@ -4615,6 +4614,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"client/node_modules/lucide-react": {
|
||||
"version": "0.394.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.394.0.tgz",
|
||||
"integrity": "sha512-PzTbJ0bsyXRhH59k5qe7MpTd5MxlpYZUcM9kGSwvPGAfnn0J6FElDwu2EX6Vuh//F7y60rcVJiFQ7EK9DCMgfw==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/node-releases": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
@@ -4637,16 +4645,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/react-resizable-panels": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.2.tgz",
|
||||
"integrity": "sha512-j4RNII75fnHkLnbsTb5G5YsDvJsSEZrJK2XSF2z0Tc2jIonYlIVir/Yh/5LvcUFCfs1HqrMAoiBFmIrRjC4XnA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"client/node_modules/ts-jest": {
|
||||
"version": "29.2.5",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz",
|
||||
@@ -4821,9 +4819,42 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ariakit/core": {
|
||||
"version": "0.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.14.tgz",
|
||||
"integrity": "sha512-hpzZvyYzGhP09S9jW1XGsU/FD5K3BKsH1eG/QJ8rfgEeUdPS7BvHPt5lHbOeJ2cMrRzBEvsEzLi1ivfDifHsVA=="
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.15.tgz",
|
||||
"integrity": "sha512-vvxmZvkNhiisKM+Y1TbGMUfVVchV/sWu9F0xw0RYADXcimWPK31dd9JnIZs/OQ5pwAryAHmERHwuGQVESkSjwQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ariakit/react": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.17.tgz",
|
||||
"integrity": "sha512-HQaIboE2axtlncJz1hRTaiQfJ1GGjhdtNcAnPwdjvl2RybfmlHowIB+HTVBp36LzroKPs/M4hPCxk7XTaqRZGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/react-core": "0.4.17"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ariakit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ariakit/react-core": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.17.tgz",
|
||||
"integrity": "sha512-kFF6n+gC/5CRQIyaMTFoBPio2xUe0k9rZhMNdUobWRmc/twfeLVkODx+8UVYaNyKilTge8G0JFqwvFKku/jKEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/core": "0.4.15",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-crypto/crc32": {
|
||||
"version": "3.0.0",
|
||||
@@ -15635,6 +15666,25 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/is-prop-valid": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "0.7.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
|
||||
@@ -16913,21 +16963,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.2.tgz",
|
||||
"integrity": "sha512-Kb3hgk9gRNRcTZktBrKdHhF3xFhYkca1Rk6e1/im2ENf83dgN54orMW0uSKTXFnUpZOUFZ+wcY05LlipwgZIFQ==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.4.tgz",
|
||||
"integrity": "sha512-lz+OGcAH1dK93rgSMzXmm1qKOJkBUqZf1L4M8TWLNplftQD3IkoEDdUFNfAn4ylsN6WOTVtWaLmvmaHOUk1dTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.16",
|
||||
"@react-aria/focus": "^3.17.1",
|
||||
"@react-aria/interactions": "^3.21.3",
|
||||
"@tanstack/react-virtual": "^3.8.1"
|
||||
"@react-aria/focus": "^3.20.2",
|
||||
"@react-aria/interactions": "^3.25.0",
|
||||
"@tanstack/react-virtual": "^3.13.9",
|
||||
"use-sync-external-store": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
@@ -20102,6 +20154,10 @@
|
||||
"resolved": "api",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@librechat/client": {
|
||||
"resolved": "packages/client",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@librechat/data-schemas": {
|
||||
"resolved": "packages/data-schemas",
|
||||
"link": true
|
||||
@@ -22727,49 +22783,43 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/focus": {
|
||||
"version": "3.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.17.1.tgz",
|
||||
"integrity": "sha512-FLTySoSNqX++u0nWZJPPN5etXY0WBxaIe/YuL/GTEeuqUIuC/2bJSaw5hlsM6T2yjy6Y/VAxBcKSdAFUlU6njQ==",
|
||||
"version": "3.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.5.tgz",
|
||||
"integrity": "sha512-JpFtXmWQ0Oca7FcvkqgjSyo6xEP7v3oQOLUId6o0xTvm4AD5W0mU2r3lYrbhsJ+XxdUUX4AVR5473sZZ85kU4A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/interactions": "^3.21.3",
|
||||
"@react-aria/utils": "^3.24.1",
|
||||
"@react-types/shared": "^3.23.1",
|
||||
"@react-aria/interactions": "^3.25.3",
|
||||
"@react-aria/utils": "^3.29.1",
|
||||
"@react-types/shared": "^3.30.0",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/focus/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/interactions": {
|
||||
"version": "3.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.21.3.tgz",
|
||||
"integrity": "sha512-BWIuf4qCs5FreDJ9AguawLVS0lV9UU+sK4CCnbCNNmYqOWY+1+gRXCsnOM32K+oMESBxilAjdHW5n1hsMqYMpA==",
|
||||
"version": "3.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.3.tgz",
|
||||
"integrity": "sha512-J1bhlrNtjPS/fe5uJQ+0c7/jiXniwa4RQlP+Emjfc/iuqpW2RhbF9ou5vROcLzWIyaW8tVMZ468J68rAs/aZ5A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/ssr": "^3.9.4",
|
||||
"@react-aria/utils": "^3.24.1",
|
||||
"@react-types/shared": "^3.23.1",
|
||||
"@react-aria/ssr": "^3.9.9",
|
||||
"@react-aria/utils": "^3.29.1",
|
||||
"@react-stately/flags": "^3.1.2",
|
||||
"@react-types/shared": "^3.30.0",
|
||||
"@swc/helpers": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/ssr": {
|
||||
"version": "3.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.4.tgz",
|
||||
"integrity": "sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==",
|
||||
"version": "3.9.9",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.9.tgz",
|
||||
"integrity": "sha512-2P5thfjfPy/np18e5wD4WPt8ydNXhij1jwA8oehxZTFqlgVMGXzcWKxTb4RtJrLFsqPO7RUQTiY8QJk0M4Vy2g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
@@ -22778,32 +22828,25 @@
|
||||
"node": ">= 12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/utils": {
|
||||
"version": "3.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.24.1.tgz",
|
||||
"integrity": "sha512-O3s9qhPMd6n42x9sKeJ3lhu5V1Tlnzhu6Yk8QOvDuXf7UGuUjXf9mzfHJt1dYzID4l9Fwm8toczBzPM9t0jc8Q==",
|
||||
"version": "3.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.29.1.tgz",
|
||||
"integrity": "sha512-yXMFVJ73rbQ/yYE/49n5Uidjw7kh192WNN9PNQGV0Xoc7EJUlSOxqhnpHmYTyO0EotJ8fdM1fMH8durHjUSI8g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/ssr": "^3.9.4",
|
||||
"@react-stately/utils": "^3.10.1",
|
||||
"@react-types/shared": "^3.23.1",
|
||||
"@react-aria/ssr": "^3.9.9",
|
||||
"@react-stately/flags": "^3.1.2",
|
||||
"@react-stately/utils": "^3.10.7",
|
||||
"@react-types/shared": "^3.30.0",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/utils/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-dnd/asap": {
|
||||
@@ -22913,25 +22956,34 @@
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-stately/flags": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
|
||||
"integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-stately/utils": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.1.tgz",
|
||||
"integrity": "sha512-VS/EHRyicef25zDZcM/ClpzYMC5i2YGN6uegOeQawmgfGjb02yaCX0F0zR69Pod9m2Hr3wunTbtpgVXvYbZItg==",
|
||||
"version": "3.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.7.tgz",
|
||||
"integrity": "sha512-cWvjGAocvy4abO9zbr6PW6taHgF24Mwy/LbQ4TC4Aq3tKdKDntxyD+sh7AkSRfJRT2ccMVaHVv2+FfHThd3PKQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-types/shared": {
|
||||
"version": "3.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.1.tgz",
|
||||
"integrity": "sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==",
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/bloom": {
|
||||
@@ -25180,12 +25232,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz",
|
||||
"integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==",
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/match-sorter-utils": {
|
||||
@@ -25279,20 +25331,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.8.2.tgz",
|
||||
"integrity": "sha512-g78+DA29K0ByAfDkuibfLQqDshf8Aha/zcyEZ+huAX/yS/TWj/CUiEY4IJfDrFacdxIFmsLm0u4VtsLSKTngRw==",
|
||||
"version": "3.13.12",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz",
|
||||
"integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.8.2"
|
||||
"@tanstack/virtual-core": "3.13.12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/table-core": {
|
||||
@@ -25308,9 +25360,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.8.2.tgz",
|
||||
"integrity": "sha512-ffpN6kTaPGwQPoWMcBAHbdv2ZCpj1SugldoYAcY0C4xH+Pej1KCOEUisNeEgbUnXOp8Y/4q6wGPu2tFHthOIQw==",
|
||||
"version": "3.13.12",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
|
||||
"integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -27942,14 +27994,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.1.tgz",
|
||||
"integrity": "sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"clsx": "1.2.1"
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://joebell.co.uk"
|
||||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
@@ -28105,9 +28159,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -31499,22 +31554,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "11.5.4",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.5.4.tgz",
|
||||
"integrity": "sha512-E+tb3/G6SO69POkdJT+3EpdMuhmtCh9EWuK4I1DnIC23L7tFPrl8vxP+LSovwaw6uUr73rUbpb4FgK011wbRJQ==",
|
||||
"version": "10.18.0",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz",
|
||||
"integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@emotion/is-prop-valid": "^0.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -32047,7 +32102,8 @@
|
||||
"node_modules/hamt_plus": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
|
||||
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
|
||||
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/handlebars": {
|
||||
"version": "4.7.8",
|
||||
@@ -34475,6 +34531,28 @@
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/jotai": {
|
||||
"version": "2.12.5",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.5.tgz",
|
||||
"integrity": "sha512-G8m32HW3lSmcz/4mbqx0hgJIQ0ekndKWiYP7kWVKi0p6saLXdSoye+FZiOFyonnd7Q482LCzm8sMDl7Ar1NWDw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=17.0.0",
|
||||
"react": ">=17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/js-base64": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
|
||||
@@ -35835,9 +35913,11 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.394.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.394.0.tgz",
|
||||
"integrity": "sha512-PzTbJ0bsyXRhH59k5qe7MpTd5MxlpYZUcM9kGSwvPGAfnn0J6FElDwu2EX6Vuh//F7y60rcVJiFQ7EK9DCMgfw==",
|
||||
"version": "0.263.1",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.263.1.tgz",
|
||||
"integrity": "sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
@@ -37516,6 +37596,21 @@
|
||||
"color-name": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "11.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
|
||||
"integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^11.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "11.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
|
||||
"integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mpath": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
|
||||
@@ -40913,6 +41008,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-resizable-panels": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.3.tgz",
|
||||
"integrity": "sha512-7HA8THVBHTzhDK4ON0tvlGXyMAJN1zBeRpuyyremSikgYh2ku6ltD7tsGQOcXx4NKPrZtYCm/5CBr+dkruTGQw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.22.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz",
|
||||
@@ -41022,6 +41127,15 @@
|
||||
"react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-virtualized/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -41126,6 +41240,7 @@
|
||||
"version": "0.7.7",
|
||||
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
|
||||
"integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hamt_plus": "1.0.2"
|
||||
},
|
||||
@@ -44951,11 +45066,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf8": {
|
||||
@@ -46690,6 +46806,61 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"packages/client": {
|
||||
"name": "@librechat/client",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@ariakit/react": "^0.4.17",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
"react-resizable-panels": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"rollup": "^4.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@radix-ui/react-separator": "^1.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^10.0.0",
|
||||
"lucide-react": "^0.263.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"packages/client/node_modules/@rollup/plugin-typescript": {
|
||||
"version": "11.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz",
|
||||
"integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"resolve": "^1.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.14.0||^3.0.0||^4.0.0",
|
||||
"tslib": "*",
|
||||
"typescript": ">=3.7.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
},
|
||||
"tslib": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"packages/data-provider": {
|
||||
"name": "librechat-data-provider",
|
||||
"version": "0.7.88",
|
||||
|
||||
44
packages/client/package.json
Normal file
44
packages/client/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@librechat/client",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"module": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
"dev": "rollup -c rollup.config.mjs -w",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@radix-ui/react-separator": "^1.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^10.0.0",
|
||||
"lucide-react": "^0.263.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"rollup": "^4.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ariakit/react": "^0.4.17",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
"react-resizable-panels": "^3.0.3"
|
||||
}
|
||||
}
|
||||
40
packages/client/rollup.config.mjs
Normal file
40
packages/client/rollup.config.mjs
Normal file
@@ -0,0 +1,40 @@
|
||||
// ESM bundler config for React components
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import alias from '@rollup/plugin-alias';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, resolve as pathResolve } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export default {
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'es',
|
||||
preserveModules: true,
|
||||
},
|
||||
external: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react/jsx-runtime',
|
||||
'@radix-ui/react-separator',
|
||||
'@radix-ui/react-slot',
|
||||
'class-variance-authority',
|
||||
'clsx',
|
||||
'framer-motion',
|
||||
'lucide-react',
|
||||
'tailwind-merge',
|
||||
],
|
||||
plugins: [
|
||||
alias({
|
||||
entries: [{ find: '~', replacement: pathResolve(__dirname, 'src') }],
|
||||
}),
|
||||
resolve(),
|
||||
typescript({
|
||||
tsconfig: './tsconfig.json',
|
||||
jsx: 'react-jsx',
|
||||
}),
|
||||
],
|
||||
};
|
||||
6
packages/client/src/common/a11y.ts
Normal file
6
packages/client/src/common/a11y.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface AnnounceOptions {
|
||||
message: string;
|
||||
isStatus?: boolean;
|
||||
}
|
||||
|
||||
export const MESSAGE_UPDATE_INTERVAL = 7000;
|
||||
33
packages/client/src/common/agents-types.ts
Normal file
33
packages/client/src/common/agents-types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { AgentCapabilities, ArtifactModes } from 'librechat-data-provider';
|
||||
import type { Agent, AgentProvider, AgentModelParameters } from 'librechat-data-provider';
|
||||
import type { OptionWithIcon, ExtendedFile } from './types';
|
||||
|
||||
export type TAgentOption = OptionWithIcon &
|
||||
Agent & {
|
||||
knowledge_files?: Array<[string, ExtendedFile]>;
|
||||
context_files?: Array<[string, ExtendedFile]>;
|
||||
code_files?: Array<[string, ExtendedFile]>;
|
||||
};
|
||||
|
||||
export type TAgentCapabilities = {
|
||||
[AgentCapabilities.web_search]: boolean;
|
||||
[AgentCapabilities.file_search]: boolean;
|
||||
[AgentCapabilities.execute_code]: boolean;
|
||||
[AgentCapabilities.end_after_tools]?: boolean;
|
||||
[AgentCapabilities.hide_sequential_outputs]?: boolean;
|
||||
};
|
||||
|
||||
export type AgentForm = {
|
||||
agent?: TAgentOption;
|
||||
id: string;
|
||||
name: string | null;
|
||||
description: string | null;
|
||||
instructions: string | null;
|
||||
model: string | null;
|
||||
model_parameters: AgentModelParameters;
|
||||
tools?: string[];
|
||||
provider?: AgentProvider | OptionWithIcon;
|
||||
agent_ids?: string[];
|
||||
[AgentCapabilities.artifacts]?: ArtifactModes | string;
|
||||
recursion_limit?: number;
|
||||
} & TAgentCapabilities;
|
||||
27
packages/client/src/common/artifacts.ts
Normal file
27
packages/client/src/common/artifacts.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export interface CodeBlock {
|
||||
id: string;
|
||||
language: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface Artifact {
|
||||
id: string;
|
||||
lastUpdateTime: number;
|
||||
index?: number;
|
||||
messageId?: string;
|
||||
identifier?: string;
|
||||
language?: string;
|
||||
content?: string;
|
||||
title?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export type ArtifactFiles =
|
||||
| {
|
||||
'App.tsx': string;
|
||||
'index.tsx': string;
|
||||
'/components/ui/MermaidDiagram.tsx': string;
|
||||
}
|
||||
| Partial<{
|
||||
[x: string]: string | undefined;
|
||||
}>;
|
||||
31
packages/client/src/common/assistants-types.ts
Normal file
31
packages/client/src/common/assistants-types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Capabilities, EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { Assistant, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { Option, ExtendedFile } from './types';
|
||||
|
||||
export type ActionsEndpoint = AssistantsEndpoint | EModelEndpoint.agents;
|
||||
|
||||
export type TAssistantOption =
|
||||
| string
|
||||
| (Option &
|
||||
Assistant & {
|
||||
files?: Array<[string, ExtendedFile]>;
|
||||
code_files?: Array<[string, ExtendedFile]>;
|
||||
});
|
||||
|
||||
export type Actions = {
|
||||
[Capabilities.code_interpreter]: boolean;
|
||||
[Capabilities.image_vision]: boolean;
|
||||
[Capabilities.retrieval]: boolean;
|
||||
};
|
||||
|
||||
export type AssistantForm = {
|
||||
assistant: TAssistantOption;
|
||||
id: string;
|
||||
name: string | null;
|
||||
description: string | null;
|
||||
instructions: string | null;
|
||||
conversation_starters: string[];
|
||||
model: string;
|
||||
functions: string[];
|
||||
append_current_datetime: boolean;
|
||||
} & Actions;
|
||||
10
packages/client/src/common/index.ts
Normal file
10
packages/client/src/common/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export {
|
||||
TShowToast,
|
||||
Option,
|
||||
OptionWithIcon,
|
||||
DropdownValueSetter,
|
||||
MentionOption,
|
||||
NotificationSeverity,
|
||||
} from './types';
|
||||
|
||||
export { MenuItemProps } from './menus';
|
||||
26
packages/client/src/common/mcp.ts
Normal file
26
packages/client/src/common/mcp.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
AuthorizationTypeEnum,
|
||||
AuthTypeEnum,
|
||||
TokenExchangeMethodEnum,
|
||||
} from 'librechat-data-provider';
|
||||
import { MCPForm } from '~/common/types';
|
||||
|
||||
export const defaultMCPFormValues: MCPForm = {
|
||||
type: AuthTypeEnum.None,
|
||||
saved_auth_fields: false,
|
||||
api_key: '',
|
||||
authorization_type: AuthorizationTypeEnum.Basic,
|
||||
custom_auth_header: '',
|
||||
oauth_client_id: '',
|
||||
oauth_client_secret: '',
|
||||
authorization_url: '',
|
||||
client_url: '',
|
||||
scope: '',
|
||||
token_exchange_method: TokenExchangeMethodEnum.DefaultPost,
|
||||
name: '',
|
||||
description: '',
|
||||
url: '',
|
||||
tools: [],
|
||||
icon: '',
|
||||
trust: false,
|
||||
};
|
||||
24
packages/client/src/common/menus.ts
Normal file
24
packages/client/src/common/menus.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type RenderProp<
|
||||
P = React.HTMLAttributes<any> & {
|
||||
ref?: React.Ref<any>;
|
||||
},
|
||||
> = (props: P) => React.ReactNode;
|
||||
|
||||
export interface MenuItemProps {
|
||||
id?: string;
|
||||
label?: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement | HTMLDivElement>) => void;
|
||||
icon?: React.ReactNode;
|
||||
kbd?: string;
|
||||
show?: boolean;
|
||||
disabled?: boolean;
|
||||
separate?: boolean;
|
||||
hideOnClick?: boolean;
|
||||
dialog?: React.ReactElement;
|
||||
ref?: React.Ref<any>;
|
||||
render?:
|
||||
| RenderProp<React.HTMLAttributes<any> & { ref?: React.Ref<any> | undefined }>
|
||||
| React.ReactElement<any, string | React.JSXElementConstructor<any>>
|
||||
| undefined;
|
||||
}
|
||||
23
packages/client/src/common/selector.ts
Normal file
23
packages/client/src/common/selector.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { TModelSpec, TStartupConfig } from 'librechat-data-provider';
|
||||
|
||||
export interface Endpoint {
|
||||
value: string;
|
||||
label: string;
|
||||
hasModels: boolean;
|
||||
models?: Array<{ name: string; isGlobal?: boolean }>;
|
||||
icon: React.ReactNode;
|
||||
agentNames?: Record<string, string>;
|
||||
assistantNames?: Record<string, string>;
|
||||
modelIcons?: Record<string, string | undefined>;
|
||||
}
|
||||
|
||||
export interface SelectedValues {
|
||||
endpoint: string | null;
|
||||
model: string | null;
|
||||
modelSpec: string | null;
|
||||
}
|
||||
|
||||
export interface ModelSelectorProps {
|
||||
startupConfig: TStartupConfig | undefined;
|
||||
}
|
||||
6
packages/client/src/common/tools.ts
Normal file
6
packages/client/src/common/tools.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { AuthType } from 'librechat-data-provider';
|
||||
|
||||
export type ApiKeyFormData = {
|
||||
apiKey: string;
|
||||
authType?: string | AuthType;
|
||||
};
|
||||
624
packages/client/src/common/types.ts
Normal file
624
packages/client/src/common/types.ts
Normal file
@@ -0,0 +1,624 @@
|
||||
import { RefObject } from 'react';
|
||||
import { FileSources, EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type * as InputNumberPrimitive from 'rc-input-number';
|
||||
import type { PrimitiveAtom, WritableAtom } from 'jotai';
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import type { TranslationKeys } from '~/hooks';
|
||||
|
||||
export type CodeBarProps = {
|
||||
lang: string;
|
||||
error?: boolean;
|
||||
plugin?: boolean;
|
||||
blockIndex?: number;
|
||||
allowExecution?: boolean;
|
||||
codeRef: RefObject<HTMLElement>;
|
||||
};
|
||||
|
||||
export enum PromptsEditorMode {
|
||||
SIMPLE = 'simple',
|
||||
ADVANCED = 'advanced',
|
||||
}
|
||||
|
||||
export enum STTEndpoints {
|
||||
browser = 'browser',
|
||||
external = 'external',
|
||||
}
|
||||
|
||||
export enum TTSEndpoints {
|
||||
browser = 'browser',
|
||||
external = 'external',
|
||||
}
|
||||
|
||||
export type AudioChunk = {
|
||||
audio: string;
|
||||
isFinal: boolean;
|
||||
alignment: {
|
||||
char_start_times_ms: number[];
|
||||
chars_durations_ms: number[];
|
||||
chars: string[];
|
||||
};
|
||||
normalizedAlignment: {
|
||||
char_start_times_ms: number[];
|
||||
chars_durations_ms: number[];
|
||||
chars: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type BadgeItem = {
|
||||
id: string;
|
||||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
||||
label: string;
|
||||
atom: PrimitiveAtom<boolean> | WritableAtom<boolean, [boolean], void>;
|
||||
isAvailable: boolean;
|
||||
};
|
||||
|
||||
export type AssistantListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: t.Assistant['metadata'];
|
||||
model: string;
|
||||
};
|
||||
|
||||
export type AgentListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar: t.Agent['avatar'];
|
||||
};
|
||||
|
||||
export type TPluginMap = Record<string, t.TPlugin>;
|
||||
|
||||
export type GenericSetter<T> = (value: T | ((currentValue: T) => T)) => void;
|
||||
|
||||
export type LastSelectedModels = Record<t.EModelEndpoint, string>;
|
||||
|
||||
export type LocalizeFunction = (
|
||||
phraseKey: TranslationKeys,
|
||||
options?: Record<string, string | number>,
|
||||
) => string;
|
||||
|
||||
export type ChatFormValues = { text: string };
|
||||
|
||||
export const mainTextareaId = 'prompt-textarea';
|
||||
export const globalAudioId = 'global-audio';
|
||||
|
||||
export enum IconContext {
|
||||
landing = 'landing',
|
||||
menuItem = 'menu-item',
|
||||
nav = 'nav',
|
||||
message = 'message',
|
||||
}
|
||||
|
||||
export type IconMapProps = {
|
||||
className?: string;
|
||||
iconURL?: string;
|
||||
context?: 'landing' | 'menu-item' | 'nav' | 'message';
|
||||
endpoint?: string | null;
|
||||
endpointType?: string;
|
||||
assistantName?: string;
|
||||
agentName?: string;
|
||||
avatar?: string;
|
||||
size?: number;
|
||||
};
|
||||
|
||||
export type IconComponent = React.ComponentType<IconMapProps>;
|
||||
export type AgentIconComponent = React.ComponentType<AgentIconMapProps>;
|
||||
export type IconComponentTypes = IconComponent | AgentIconComponent;
|
||||
export type IconsRecord = {
|
||||
[key in t.EModelEndpoint | 'unknown' | string]: IconComponentTypes | null | undefined;
|
||||
};
|
||||
|
||||
export type AgentIconMapProps = IconMapProps & { agentName?: string };
|
||||
|
||||
export type NavLink = {
|
||||
title: TranslationKeys;
|
||||
label?: string;
|
||||
icon: LucideIcon | React.FC;
|
||||
Component?: React.ComponentType;
|
||||
onClick?: (e?: React.MouseEvent) => void;
|
||||
variant?: 'default' | 'ghost';
|
||||
id: string;
|
||||
};
|
||||
|
||||
export interface NavProps {
|
||||
isCollapsed: boolean;
|
||||
links: NavLink[];
|
||||
resize?: (size: number) => void;
|
||||
defaultActive?: string;
|
||||
}
|
||||
|
||||
export interface DataColumnMeta {
|
||||
meta:
|
||||
| {
|
||||
size: number | string;
|
||||
}
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export enum Panel {
|
||||
advanced = 'advanced',
|
||||
builder = 'builder',
|
||||
actions = 'actions',
|
||||
model = 'model',
|
||||
version = 'version',
|
||||
mcp = 'mcp',
|
||||
}
|
||||
|
||||
export type FileSetter =
|
||||
| GenericSetter<Map<string, ExtendedFile>>
|
||||
| React.Dispatch<React.SetStateAction<Map<string, ExtendedFile>>>;
|
||||
|
||||
export type ActionAuthForm = {
|
||||
/* General */
|
||||
type: t.AuthTypeEnum;
|
||||
saved_auth_fields: boolean;
|
||||
/* API key */
|
||||
api_key: string; // not nested
|
||||
authorization_type: t.AuthorizationTypeEnum;
|
||||
custom_auth_header: string;
|
||||
/* OAuth */
|
||||
oauth_client_id: string; // not nested
|
||||
oauth_client_secret: string; // not nested
|
||||
authorization_url: string;
|
||||
client_url: string;
|
||||
scope: string;
|
||||
token_exchange_method: t.TokenExchangeMethodEnum;
|
||||
};
|
||||
|
||||
export type MCPForm = ActionAuthForm & {
|
||||
name?: string;
|
||||
description?: string;
|
||||
url?: string;
|
||||
tools?: string[];
|
||||
icon?: string;
|
||||
trust?: boolean;
|
||||
};
|
||||
|
||||
export type ActionWithNullableMetadata = Omit<t.Action, 'metadata'> & {
|
||||
metadata: t.ActionMetadata | null;
|
||||
};
|
||||
|
||||
export type AssistantPanelProps = {
|
||||
index?: number;
|
||||
action?: ActionWithNullableMetadata;
|
||||
actions?: t.Action[];
|
||||
assistant_id?: string;
|
||||
activePanel?: string;
|
||||
endpoint: t.AssistantsEndpoint;
|
||||
version: number | string;
|
||||
documentsMap: Map<string, t.AssistantDocument> | null;
|
||||
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
||||
setCurrentAssistantId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
};
|
||||
|
||||
export type AgentPanelProps = {
|
||||
index?: number;
|
||||
agent_id?: string;
|
||||
activePanel?: string;
|
||||
mcp?: t.MCP;
|
||||
mcps?: t.MCP[];
|
||||
action?: t.Action;
|
||||
actions?: t.Action[];
|
||||
createMutation: UseMutationResult<t.Agent, Error, t.AgentCreateParams>;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
setMcp: React.Dispatch<React.SetStateAction<t.MCP | undefined>>;
|
||||
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
||||
endpointsConfig?: t.TEndpointsConfig;
|
||||
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
agentsConfig?: t.TAgentsEndpoint | null;
|
||||
};
|
||||
|
||||
export type AgentPanelContextType = {
|
||||
action?: t.Action;
|
||||
actions?: t.Action[];
|
||||
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
||||
mcp?: t.MCP;
|
||||
mcps?: t.MCP[];
|
||||
setMcp: React.Dispatch<React.SetStateAction<t.MCP | undefined>>;
|
||||
setMcps: React.Dispatch<React.SetStateAction<t.MCP[] | undefined>>;
|
||||
groupedTools: Record<string, t.AgentToolType & { tools?: t.AgentToolType[] }>;
|
||||
tools: t.AgentToolType[];
|
||||
activePanel?: string;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
agent_id?: string;
|
||||
};
|
||||
|
||||
export type AgentModelPanelProps = {
|
||||
agent_id?: string;
|
||||
providers: Option[];
|
||||
models: Record<string, string[] | undefined>;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
};
|
||||
|
||||
export type AugmentedColumnDef<TData, TValue> = ColumnDef<TData, TValue> & DataColumnMeta;
|
||||
|
||||
export type TSetOption = t.TSetOption;
|
||||
|
||||
export type TSetExample = (
|
||||
i: number,
|
||||
type: string,
|
||||
newValue: number | string | boolean | null,
|
||||
) => void;
|
||||
|
||||
export type OnInputNumberChange = InputNumberPrimitive.InputNumberProps['onChange'];
|
||||
|
||||
export const defaultDebouncedDelay = 450;
|
||||
|
||||
export enum ESide {
|
||||
Top = 'top',
|
||||
Right = 'right',
|
||||
Bottom = 'bottom',
|
||||
Left = 'left',
|
||||
}
|
||||
|
||||
export enum NotificationSeverity {
|
||||
INFO = 'info',
|
||||
SUCCESS = 'success',
|
||||
WARNING = 'warning',
|
||||
ERROR = 'error',
|
||||
}
|
||||
|
||||
export type TShowToast = {
|
||||
message: string;
|
||||
severity?: NotificationSeverity;
|
||||
showIcon?: boolean;
|
||||
duration?: number;
|
||||
status?: 'error' | 'success' | 'warning' | 'info';
|
||||
};
|
||||
|
||||
export type TBaseSettingsProps = {
|
||||
conversation: t.TConversation | t.TPreset | null;
|
||||
className?: string;
|
||||
isPreset?: boolean;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export type TSettingsProps = TBaseSettingsProps & {
|
||||
setOption: TSetOption;
|
||||
};
|
||||
|
||||
export type TModels = {
|
||||
models: string[];
|
||||
showAbove?: boolean;
|
||||
popover?: boolean;
|
||||
};
|
||||
|
||||
export type TModelSelectProps = TSettingsProps & TModels;
|
||||
|
||||
export type TEditPresetProps = {
|
||||
open: boolean;
|
||||
onOpenChange: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
preset: t.TPreset;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export type TSetOptions = (options: Record<string, unknown>) => void;
|
||||
export type TSetOptionsPayload = {
|
||||
setOption: TSetOption;
|
||||
setExample: TSetExample;
|
||||
addExample: () => void;
|
||||
removeExample: () => void;
|
||||
setAgentOption: TSetOption;
|
||||
// getConversation: () => t.TConversation | t.TPreset | null;
|
||||
checkPluginSelection: (value: string) => boolean;
|
||||
setTools: (newValue: string, remove?: boolean) => void;
|
||||
setOptions?: TSetOptions;
|
||||
};
|
||||
|
||||
export type TPresetItemProps = {
|
||||
preset: t.TPreset;
|
||||
value: t.TPreset;
|
||||
onSelect: (preset: t.TPreset) => void;
|
||||
onChangePreset: (preset: t.TPreset) => void;
|
||||
onDeletePreset: (preset: t.TPreset) => void;
|
||||
};
|
||||
|
||||
export type TOnClick = (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
|
||||
export type TGenButtonProps = {
|
||||
onClick: TOnClick;
|
||||
};
|
||||
|
||||
export type TAskProps = {
|
||||
text: string;
|
||||
overrideConvoId?: string;
|
||||
overrideUserMessageId?: string;
|
||||
parentMessageId?: string | null;
|
||||
conversationId?: string | null;
|
||||
messageId?: string | null;
|
||||
clientTimestamp?: string;
|
||||
};
|
||||
|
||||
export type TOptions = {
|
||||
editedMessageId?: string | null;
|
||||
editedText?: string | null;
|
||||
isRegenerate?: boolean;
|
||||
isContinued?: boolean;
|
||||
isEdited?: boolean;
|
||||
overrideMessages?: t.TMessage[];
|
||||
/** This value is only true when the user submits a message with "Save & Submit" for a user-created message */
|
||||
isResubmission?: boolean;
|
||||
/** Currently only utilized when `isResubmission === true`, uses that message's currently attached files */
|
||||
overrideFiles?: t.TMessage['files'];
|
||||
};
|
||||
|
||||
export type TAskFunction = (props: TAskProps, options?: TOptions) => void;
|
||||
|
||||
export type TMessageProps = {
|
||||
conversation?: t.TConversation | null;
|
||||
messageId?: string | null;
|
||||
message?: t.TMessage;
|
||||
messagesTree?: t.TMessage[];
|
||||
currentEditId: string | number | null;
|
||||
isSearchView?: boolean;
|
||||
siblingIdx?: number;
|
||||
siblingCount?: number;
|
||||
setCurrentEditId?: React.Dispatch<React.SetStateAction<string | number | null>> | null;
|
||||
setSiblingIdx?: ((value: number) => void | React.Dispatch<React.SetStateAction<number>>) | null;
|
||||
};
|
||||
|
||||
export type TMessageIcon = { endpoint?: string | null; isCreatedByUser?: boolean } & Pick<
|
||||
t.TConversation,
|
||||
'modelLabel'
|
||||
> &
|
||||
Pick<t.TMessage, 'model' | 'iconURL'>;
|
||||
|
||||
export type TInitialProps = {
|
||||
text: string;
|
||||
edit: boolean;
|
||||
error: boolean;
|
||||
unfinished: boolean;
|
||||
isSubmitting: boolean;
|
||||
isLast: boolean;
|
||||
};
|
||||
export type TAdditionalProps = {
|
||||
ask: TAskFunction;
|
||||
message: t.TMessage;
|
||||
isCreatedByUser: boolean;
|
||||
siblingIdx: number;
|
||||
enterEdit: (cancel: boolean) => void;
|
||||
setSiblingIdx: (value: number) => void;
|
||||
};
|
||||
|
||||
export type TMessageContentProps = TInitialProps & TAdditionalProps;
|
||||
|
||||
export type TText = Pick<TInitialProps, 'text'> & { className?: string };
|
||||
export type TEditProps = Pick<TInitialProps, 'isSubmitting'> &
|
||||
Omit<TAdditionalProps, 'isCreatedByUser' | 'siblingIdx'> & {
|
||||
text?: string;
|
||||
index?: number;
|
||||
siblingIdx: number | null;
|
||||
};
|
||||
export type TDisplayProps = TText &
|
||||
Pick<TAdditionalProps, 'isCreatedByUser' | 'message'> & {
|
||||
showCursor?: boolean;
|
||||
};
|
||||
|
||||
export type TConfigProps = {
|
||||
userKey: string;
|
||||
setUserKey: React.Dispatch<React.SetStateAction<string>>;
|
||||
endpoint: t.EModelEndpoint | string;
|
||||
};
|
||||
|
||||
export type TDangerButtonProps = {
|
||||
id: string;
|
||||
confirmClear: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
showText?: boolean;
|
||||
mutation?: UseMutationResult<unknown>;
|
||||
onClick: () => void;
|
||||
infoTextCode: TranslationKeys;
|
||||
actionTextCode: TranslationKeys;
|
||||
dataTestIdInitial: string;
|
||||
dataTestIdConfirm: string;
|
||||
infoDescriptionCode?: TranslationKeys;
|
||||
confirmActionTextCode?: TranslationKeys;
|
||||
};
|
||||
|
||||
export type TDialogProps = {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type TPluginStoreDialogProps = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type TResError = {
|
||||
response: { data: { message: string } };
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type TAuthContext = {
|
||||
user: t.TUser | undefined;
|
||||
token: string | undefined;
|
||||
isAuthenticated: boolean;
|
||||
error: string | undefined;
|
||||
login: (data: t.TLoginUser) => void;
|
||||
logout: (redirect?: string) => void;
|
||||
setError: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
roles?: Record<string, t.TRole | null | undefined>;
|
||||
};
|
||||
|
||||
export type TUserContext = {
|
||||
user?: t.TUser | undefined;
|
||||
token: string | undefined;
|
||||
isAuthenticated: boolean;
|
||||
redirect?: string;
|
||||
};
|
||||
|
||||
export type TAuthConfig = {
|
||||
loginRedirect: string;
|
||||
test?: boolean;
|
||||
};
|
||||
|
||||
export type IconProps = Pick<t.TMessage, 'isCreatedByUser' | 'model'> &
|
||||
Pick<t.TConversation, 'chatGptLabel' | 'modelLabel'> & {
|
||||
size?: number;
|
||||
button?: boolean;
|
||||
iconURL?: string;
|
||||
message?: boolean;
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
endpoint?: t.EModelEndpoint | string | null;
|
||||
endpointType?: t.EModelEndpoint | null;
|
||||
assistantName?: string;
|
||||
agentName?: string;
|
||||
error?: boolean;
|
||||
};
|
||||
|
||||
export type Option = Record<string, unknown> & {
|
||||
label?: string;
|
||||
value: string | number | null;
|
||||
};
|
||||
|
||||
export type StringOption = Option & { value: string | null };
|
||||
|
||||
export type VoiceOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type TMessageAudio = {
|
||||
isLast?: boolean;
|
||||
index: number;
|
||||
messageId: string;
|
||||
content: string;
|
||||
className?: string;
|
||||
renderButton?: (props: {
|
||||
onClick: (e?: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
title: string;
|
||||
icon: React.ReactNode;
|
||||
isActive?: boolean;
|
||||
isVisible?: boolean;
|
||||
isDisabled?: boolean;
|
||||
className?: string;
|
||||
}) => React.ReactNode;
|
||||
};
|
||||
|
||||
export type OptionWithIcon = Option & { icon?: React.ReactNode };
|
||||
export type DropdownValueSetter = (value: string | Option | OptionWithIcon) => void;
|
||||
export type MentionOption = OptionWithIcon & {
|
||||
type: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
};
|
||||
export type PromptOption = MentionOption & {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type TOptionSettings = {
|
||||
showExamples?: boolean;
|
||||
isCodeChat?: boolean;
|
||||
};
|
||||
|
||||
export interface ExtendedFile {
|
||||
file?: File;
|
||||
file_id: string;
|
||||
temp_file_id?: string;
|
||||
type?: string;
|
||||
filepath?: string;
|
||||
filename?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
size: number;
|
||||
preview?: string;
|
||||
progress: number;
|
||||
source?: FileSources;
|
||||
attached?: boolean;
|
||||
embedded?: boolean;
|
||||
tool_resource?: string;
|
||||
metadata?: t.TFile['metadata'];
|
||||
}
|
||||
|
||||
export interface ModelItemProps {
|
||||
modelName: string;
|
||||
endpoint: EModelEndpoint;
|
||||
isSelected: boolean;
|
||||
onSelect: () => void;
|
||||
onNavigateBack: () => void;
|
||||
icon?: JSX.Element;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export type ContextType = {
|
||||
navVisible: boolean;
|
||||
setNavVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
export interface SwitcherProps {
|
||||
endpoint?: t.EModelEndpoint | null;
|
||||
endpointKeyProvided: boolean;
|
||||
isCollapsed: boolean;
|
||||
}
|
||||
export type TLoginLayoutContext = {
|
||||
startupConfig: t.TStartupConfig | null;
|
||||
startupConfigError: unknown;
|
||||
isFetching: boolean;
|
||||
error: string | null;
|
||||
setError: React.Dispatch<React.SetStateAction<string | null>>;
|
||||
headerText: string;
|
||||
setHeaderText: React.Dispatch<React.SetStateAction<string>>;
|
||||
};
|
||||
|
||||
export type NewConversationParams = {
|
||||
template?: Partial<t.TConversation>;
|
||||
preset?: Partial<t.TPreset>;
|
||||
modelsData?: t.TModelsConfig;
|
||||
buildDefault?: boolean;
|
||||
keepLatestMessage?: boolean;
|
||||
keepAddedConvos?: boolean;
|
||||
disableParams?: boolean;
|
||||
};
|
||||
|
||||
export type ConvoGenerator = (params: NewConversationParams) => void | t.TConversation;
|
||||
|
||||
export type TBaseResData = {
|
||||
plugin?: t.TResPlugin;
|
||||
final?: boolean;
|
||||
initial?: boolean;
|
||||
previousMessages?: t.TMessage[];
|
||||
conversation: t.TConversation;
|
||||
conversationId?: string;
|
||||
runMessages?: t.TMessage[];
|
||||
};
|
||||
|
||||
export type TResData = TBaseResData & {
|
||||
requestMessage: t.TMessage;
|
||||
responseMessage: t.TMessage;
|
||||
};
|
||||
|
||||
export type TFinalResData = Omit<TBaseResData, 'conversation'> & {
|
||||
conversation: Partial<t.TConversation> & Pick<t.TConversation, 'conversationId'>;
|
||||
requestMessage?: t.TMessage;
|
||||
responseMessage?: t.TMessage;
|
||||
};
|
||||
|
||||
export type TVectorStore = {
|
||||
_id: string;
|
||||
object: 'vector_store';
|
||||
created_at: string | Date;
|
||||
name: string;
|
||||
bytes?: number;
|
||||
file_counts?: {
|
||||
in_progress: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
cancelled: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type TThread = { id: string; createdAt: string };
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
google_tag_manager?: unknown;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
@@ -28,7 +27,7 @@ const AccordionTrigger = React.forwardRef<
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
<ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
||||
|
||||
import { cn } from '../../utils';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root;
|
||||
|
||||
79
packages/client/src/components/AnimatedSearchInput.tsx
Normal file
79
packages/client/src/components/AnimatedSearchInput.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import { Search } from 'lucide-react';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AnimatedSearchInput = ({
|
||||
value,
|
||||
onChange,
|
||||
isSearching: searching,
|
||||
placeholder,
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
isSearching?: boolean;
|
||||
placeholder: string;
|
||||
}) => {
|
||||
const isSearching = searching === true;
|
||||
const hasValue = value != null && value.length > 0;
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="relative rounded-lg transition-all duration-500 ease-in-out">
|
||||
<div className="relative">
|
||||
{/* Icon on the left */}
|
||||
<div className="absolute left-3 top-1/2 z-50 -translate-y-1/2">
|
||||
<Search
|
||||
className={cn(
|
||||
`h-4 w-4 transition-all duration-500 ease-in-out`,
|
||||
isSearching && hasValue ? 'text-blue-400' : 'text-gray-400',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Input field */}
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
className={`peer relative z-20 w-full rounded-lg bg-surface-secondary px-10 py-2 outline-none ring-0 backdrop-blur-sm transition-all duration-500 ease-in-out placeholder:text-gray-400 focus:outline-none focus:ring-0`}
|
||||
/>
|
||||
|
||||
{/* Gradient overlay */}
|
||||
<div
|
||||
className={`pointer-events-none absolute inset-0 z-20 rounded-lg bg-gradient-to-r from-blue-500/20 via-purple-500/20 to-blue-500/20 transition-all duration-500 ease-in-out ${isSearching && hasValue ? 'opacity-100 blur-sm' : 'opacity-0 blur-none'} `}
|
||||
/>
|
||||
|
||||
{/* Animated loading indicator */}
|
||||
<div
|
||||
className={`absolute right-3 top-1/2 z-20 -translate-y-1/2 transition-all duration-500 ease-in-out ${isSearching && hasValue ? 'scale-100 opacity-100' : 'scale-0 opacity-0'} `}
|
||||
>
|
||||
<div className="relative h-2 w-2">
|
||||
<div className="absolute inset-0 animate-ping rounded-full bg-blue-500/60" />
|
||||
<div className="absolute inset-0 rounded-full bg-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Outer glow effect */}
|
||||
<div
|
||||
className={`absolute -inset-8 -z-10 transition-all duration-700 ease-in-out ${isSearching && hasValue ? 'scale-105 opacity-100' : 'scale-100 opacity-0'} `}
|
||||
>
|
||||
<div className="absolute inset-0">
|
||||
<div
|
||||
className={`bg-gradient-radial absolute inset-0 from-blue-500/10 to-transparent transition-opacity duration-700 ease-in-out ${isSearching && hasValue ? 'animate-pulse-slow opacity-100' : 'opacity-0'} `}
|
||||
/>
|
||||
<div
|
||||
className={`absolute inset-0 bg-gradient-to-r from-purple-500/5 via-blue-500/5 to-purple-500/5 blur-xl transition-all duration-700 ease-in-out ${isSearching && hasValue ? 'animate-gradient-x opacity-100' : 'opacity-0'} `}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`absolute inset-0 -z-20 scale-100 bg-gradient-to-r from-blue-500/10 via-purple-500/10 to-blue-500/10 opacity-0 blur-xl transition-all duration-500 ease-in-out peer-focus:scale-105 peer-focus:opacity-100`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnimatedSearchInput;
|
||||
@@ -26,7 +26,7 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.animated-tab[data-state="active"] {
|
||||
.animated-tab[data-state='active'] {
|
||||
border-bottom-color: transparent !important;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import type React from 'react';
|
||||
|
||||
import { X, Plus } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { ButtonHTMLAttributes } from 'react';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
interface BadgeProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
interface BadgeProps
|
||||
extends Omit<
|
||||
ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
'onAnimationStart' | 'onDragStart' | 'onDragEnd' | 'onDrag'
|
||||
> {
|
||||
icon?: LucideIcon;
|
||||
label: string;
|
||||
id?: string;
|
||||
@@ -71,7 +74,7 @@ export default function Badge({
|
||||
}}
|
||||
whileTap={{ scale: isDragging ? 1.1 : isDisabled ? 1 : 0.97 }}
|
||||
transition={{ type: 'tween', duration: 0.1, ease: 'easeOut' }}
|
||||
{...props}
|
||||
{...(props as React.ComponentProps<typeof motion.button>)}
|
||||
>
|
||||
{Icon && <Icon className={cn('relative h-5 w-5 md:h-4 md:w-4', !label && 'mx-auto')} />}
|
||||
<span className="relative hidden md:inline">{label}</span>
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { ChevronRight, MoreHorizontal } from 'lucide-react';
|
||||
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Breadcrumb = React.forwardRef<
|
||||
@@ -17,7 +16,7 @@ const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWi
|
||||
<ol
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-muted-foreground flex flex-wrap items-center gap-1.5 break-words text-sm sm:gap-2.5',
|
||||
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -44,7 +43,7 @@ const BreadcrumbLink = React.forwardRef<
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
className={cn('hover:text-foreground transition-colors', className)}
|
||||
className={cn('transition-colors hover:text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -58,7 +57,7 @@ const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.ComponentPropsWit
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn('text-foreground font-normal', className)}
|
||||
className={cn('font-normal text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||
import { Check } from 'lucide-react';
|
||||
import { cn } from '../../utils';
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { Checkbox, useStoreState, useCheckboxStore } from '@ariakit/react';
|
||||
import { cn } from '~/utils';
|
||||
import * as React from 'react';
|
||||
|
||||
const CheckboxButton = React.forwardRef<
|
||||
HTMLInputElement,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@ariakit/react';
|
||||
import type { OptionWithIcon } from '~/common';
|
||||
import { SelectTrigger, SelectValue, SelectScrollDownButton } from './Select';
|
||||
import useCombobox from '~/hooks/Input/useCombobox';
|
||||
import { useCombobox } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export default function ComboboxComponent({
|
||||
@@ -81,7 +81,7 @@ export default function ComboboxComponent({
|
||||
isCollapsed
|
||||
? 'flex h-9 w-9 shrink-0 items-center justify-center p-0 [&>span]:w-auto [&>svg]:hidden'
|
||||
: '',
|
||||
'bg-white text-black hover:bg-gray-50 focus-visible:ring-2 focus-visible:ring-gray-500 dark:bg-gray-850 dark:text-white ',
|
||||
'bg-white text-black hover:bg-gray-50 focus-visible:ring-2 focus-visible:ring-gray-500 dark:bg-gray-850 dark:text-white',
|
||||
)}
|
||||
>
|
||||
<SelectValue placeholder={selectPlaceholder}>
|
||||
@@ -93,7 +93,7 @@ export default function ComboboxComponent({
|
||||
style={{ userSelect: 'none' }}
|
||||
>
|
||||
{selectedValue
|
||||
? displayValue ?? selectedValue
|
||||
? (displayValue ?? selectedValue)
|
||||
: selectPlaceholder && selectPlaceholder}
|
||||
</span>
|
||||
</SelectValue>
|
||||
@@ -140,7 +140,7 @@ export default function ComboboxComponent({
|
||||
<RadixSelect.Item key={value} value={`${value ?? ''}`} asChild>
|
||||
<ComboboxItem
|
||||
className={cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'rounded-lg hover:bg-gray-100/50 hover:bg-gray-50 dark:text-white dark:hover:bg-gray-600',
|
||||
)}
|
||||
/** Hacky fix for radix-ui Android issue: https://github.com/radix-ui/primitives/issues/1658 */
|
||||
@@ -155,8 +155,8 @@ export default function ComboboxComponent({
|
||||
</RadixSelect.ItemIndicator>
|
||||
</span>
|
||||
<RadixSelect.ItemText>
|
||||
<div className="[&_svg]:text-foreground flex items-center justify-center gap-3 dark:text-white [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0">
|
||||
<div className="assistant-item overflow-hidden rounded-full ">
|
||||
<div className="flex items-center justify-center gap-3 dark:text-white [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0 [&_svg]:text-foreground">
|
||||
<div className="assistant-item overflow-hidden rounded-full">
|
||||
{icon && icon}
|
||||
</div>
|
||||
{label}
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useCallback, useEffect, useRef, useState, memo, useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import {
|
||||
Row,
|
||||
@@ -26,11 +25,9 @@ import {
|
||||
AnimatedSearchInput,
|
||||
Skeleton,
|
||||
} from './';
|
||||
import { TrashIcon, Spinner } from '~/components/svg';
|
||||
import { useLocalize, useMediaQuery } from '~/hooks';
|
||||
import { LocalizeFunction } from '~/common';
|
||||
import { TrashIcon, Spinner } from '~/svgs';
|
||||
import { useMediaQuery } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
type TableColumn<TData, TValue> = ColumnDef<TData, TValue> & {
|
||||
meta?: {
|
||||
@@ -81,6 +78,7 @@ interface DataTableProps<TData, TValue> {
|
||||
onFilterChange?: (value: string) => void;
|
||||
filterValue?: string;
|
||||
isLoading?: boolean;
|
||||
enableSearch?: boolean;
|
||||
}
|
||||
|
||||
const TableRowComponent = <TData, TValue>({
|
||||
@@ -165,13 +163,11 @@ const DeleteButton = memo(
|
||||
isDeleting,
|
||||
disabled,
|
||||
isSmallScreen,
|
||||
localize,
|
||||
}: {
|
||||
onDelete?: () => Promise<void>;
|
||||
isDeleting: boolean;
|
||||
disabled: boolean;
|
||||
isSmallScreen: boolean;
|
||||
localize: LocalizeFunction;
|
||||
}) => {
|
||||
if (!onDelete) {
|
||||
return null;
|
||||
@@ -188,7 +184,7 @@ const DeleteButton = memo(
|
||||
) : (
|
||||
<>
|
||||
<TrashIcon className="size-3.5 text-red-400 sm:size-4" />
|
||||
{!isSmallScreen && <span className="ml-2">{localize('com_ui_delete')}</span>}
|
||||
{!isSmallScreen && <span className="ml-2">Delete</span>}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
@@ -211,12 +207,11 @@ export default function DataTable<TData, TValue>({
|
||||
onFilterChange,
|
||||
filterValue,
|
||||
isLoading,
|
||||
enableSearch = true,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const localize = useLocalize();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||
const tableContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const search = useRecoilValue(store.search);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
||||
const [sorting, setSorting] = useState<SortingState>(defaultSort);
|
||||
@@ -371,16 +366,15 @@ export default function DataTable<TData, TValue>({
|
||||
isDeleting={isDeleting}
|
||||
disabled={!table.getFilteredSelectedRowModel().rows.length || isDeleting}
|
||||
isSmallScreen={isSmallScreen}
|
||||
localize={localize}
|
||||
/>
|
||||
)}
|
||||
{filterColumn !== undefined && table.getColumn(filterColumn) && search.enabled && (
|
||||
{filterColumn !== undefined && table.getColumn(filterColumn) && enableSearch && (
|
||||
<div className="relative flex-1">
|
||||
<AnimatedSearchInput
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
isSearching={isSearching}
|
||||
placeholder={`${localize('com_ui_search')}...`}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -448,7 +442,7 @@ export default function DataTable<TData, TValue>({
|
||||
{!virtualRows.length && (
|
||||
<TableRow className="hover:bg-transparent">
|
||||
<TableCell colSpan={columns.length} className="p-4 text-center">
|
||||
{localize('com_ui_no_data')}
|
||||
No data available
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
@@ -1,8 +1,5 @@
|
||||
import { ArrowDownIcon, ArrowUpIcon, CaretSortIcon, EyeNoneIcon } from '@radix-ui/react-icons';
|
||||
import { Column } from '@tanstack/react-table';
|
||||
|
||||
import { cn } from '~/utils';
|
||||
import { Button } from './Button';
|
||||
import { ArrowDownIcon, ArrowUpIcon, CaretSortIcon, EyeNoneIcon } from '@radix-ui/react-icons';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -10,6 +7,8 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from './DropdownMenu';
|
||||
import { Button } from './Button';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
interface DataTableColumnHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLDivElement> {
|
||||
column: Column<TData, TValue>;
|
||||
@@ -29,7 +28,7 @@ export function DataTableColumnHeader<TData, TValue>({
|
||||
<div className={cn('flex items-center space-x-2', className)}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="sm" className="data-[state=open]:bg-accent -ml-3 h-8">
|
||||
<Button variant="ghost" size="sm" className="-ml-3 h-8 data-[state=open]:bg-accent">
|
||||
<span>{title}</span>
|
||||
{column.getIsSorted() === 'desc' ? (
|
||||
<ArrowDownIcon className="ml-2 h-4 w-4" />
|
||||
@@ -42,16 +41,16 @@ export function DataTableColumnHeader<TData, TValue>({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="z-[1001]">
|
||||
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
|
||||
<ArrowUpIcon className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" />
|
||||
<ArrowUpIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Asc
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
|
||||
<ArrowDownIcon className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" />
|
||||
<ArrowDownIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Desc
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
|
||||
<EyeNoneIcon className="text-muted-foreground/70 mr-2 h-3.5 w-3.5" />
|
||||
<EyeNoneIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
||||
Hide
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
12
packages/client/src/components/DelayedRender.tsx
Normal file
12
packages/client/src/components/DelayedRender.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import { useDelayedRender } from '~/hooks';
|
||||
|
||||
interface DelayedRenderProps {
|
||||
delay: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const DelayedRender = ({ delay, children }: DelayedRenderProps) =>
|
||||
useDelayedRender(delay)(() => children);
|
||||
|
||||
export default DelayedRender;
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { Button } from '../ui/Button';
|
||||
import { useMediaQuery } from '~/hooks';
|
||||
import { Button } from './Button';
|
||||
import { X } from 'lucide-react';
|
||||
import { cn } from '~/utils';
|
||||
import { useMediaQuery } from '~/hooks';
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import 'test/matchMedia.mock';
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import DialogTemplate from './DialogTemplate';
|
||||
import { Dialog } from '@radix-ui/react-dialog';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { Provider } from 'jotai';
|
||||
|
||||
describe('DialogTemplate', () => {
|
||||
let mockSelectHandler;
|
||||
let mockSelectHandler: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSelectHandler = jest.fn();
|
||||
@@ -15,7 +14,7 @@ describe('DialogTemplate', () => {
|
||||
|
||||
it('renders correctly with all props', () => {
|
||||
const { getByText } = render(
|
||||
<RecoilRoot>
|
||||
<Provider>
|
||||
<Dialog
|
||||
open
|
||||
data-testid="test-dialog"
|
||||
@@ -32,7 +31,7 @@ describe('DialogTemplate', () => {
|
||||
selection={{ selectHandler: mockSelectHandler, selectText: 'Select' }}
|
||||
/>
|
||||
</Dialog>
|
||||
</RecoilRoot>,
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(getByText('Test Dialog')).toBeInTheDocument();
|
||||
@@ -46,14 +45,14 @@ describe('DialogTemplate', () => {
|
||||
|
||||
it('renders correctly without optional props', () => {
|
||||
const { queryByText } = render(
|
||||
<RecoilRoot>
|
||||
<Provider>
|
||||
<Dialog
|
||||
open
|
||||
onOpenChange={() => {
|
||||
return;
|
||||
}}
|
||||
></Dialog>
|
||||
</RecoilRoot>,
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(queryByText('Test Dialog')).toBeNull();
|
||||
@@ -67,7 +66,7 @@ describe('DialogTemplate', () => {
|
||||
|
||||
it('calls selectHandler when the select button is clicked', () => {
|
||||
const { getByText } = render(
|
||||
<RecoilRoot>
|
||||
<Provider>
|
||||
<Dialog
|
||||
open
|
||||
onOpenChange={() => {
|
||||
@@ -79,7 +78,7 @@ describe('DialogTemplate', () => {
|
||||
selection={{ selectHandler: mockSelectHandler, selectText: 'Select' }}
|
||||
/>
|
||||
</Dialog>
|
||||
</RecoilRoot>,
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('Select'));
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
DialogTitle,
|
||||
} from './';
|
||||
import { cn } from '~/utils/';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
type SelectionProps = {
|
||||
selectHandler?: () => void;
|
||||
@@ -31,7 +30,6 @@ type DialogTemplateProps = {
|
||||
};
|
||||
|
||||
const DialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivElement>) => {
|
||||
const localize = useLocalize();
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
@@ -46,7 +44,7 @@ const DialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivE
|
||||
showCancelButton = true,
|
||||
} = props;
|
||||
const { selectHandler, selectClasses, selectText } = selection || {};
|
||||
const Cancel = localize('com_ui_cancel');
|
||||
const Cancel = 'cancel';
|
||||
|
||||
const defaultSelect =
|
||||
'bg-gray-800 text-white transition-colors hover:bg-gray-700 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-200 dark:text-gray-800 dark:hover:bg-gray-200';
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
ListboxOptions,
|
||||
Transition,
|
||||
} from '@headlessui/react';
|
||||
import { AnchorPropsWithSelection } from '@headlessui/react/dist/internal/floating';
|
||||
import type { Option } from '~/common';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
@@ -16,7 +15,6 @@ interface DropdownProps {
|
||||
onChange: (value: string | Option) => void;
|
||||
options: (string | Option)[];
|
||||
className?: string;
|
||||
anchor?: AnchorPropsWithSelection;
|
||||
sizeClasses?: string;
|
||||
testId?: string;
|
||||
}
|
||||
@@ -31,7 +29,6 @@ const Dropdown: FC<DropdownProps> = ({
|
||||
onChange,
|
||||
options,
|
||||
className = '',
|
||||
anchor,
|
||||
sizeClasses,
|
||||
testId = 'dropdown-menu',
|
||||
}) => {
|
||||
@@ -39,7 +36,7 @@ const Dropdown: FC<DropdownProps> = ({
|
||||
typeof option === 'string' ? option : option?.value;
|
||||
|
||||
const getDisplay = (option?: string | Option) =>
|
||||
typeof option === 'string' ? option : option?.label ?? option?.value;
|
||||
typeof option === 'string' ? option : (option?.label ?? option?.value);
|
||||
|
||||
const isEqual = (a: string | Option, b: string | Option): boolean => getValue(a) === getValue(b);
|
||||
|
||||
@@ -90,7 +87,6 @@ const Dropdown: FC<DropdownProps> = ({
|
||||
sizeClasses,
|
||||
className,
|
||||
)}
|
||||
anchor={anchor}
|
||||
aria-label="List of options"
|
||||
>
|
||||
{options.map((item, index) => (
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Label, Input } from '~/components/ui';
|
||||
import { Label } from './Label';
|
||||
import { Input } from './Input';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export default function FormInput({
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
|
||||
|
||||
import { cn } from '../../utils';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const HoverCard = HoverCardPrimitive.Root;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Input } from '~/components/ui/Input';
|
||||
import { Input } from './Input';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export type InputWithDropdownProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
||||
@@ -92,7 +92,7 @@ const InputWithDropdown = React.forwardRef<HTMLInputElement, InputWithDropdownPr
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="text-tertiary hover:text-secondary absolute inset-y-0 right-0 flex items-center rounded-md px-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring-primary"
|
||||
className="text-tertiary absolute inset-y-0 right-0 flex items-center rounded-md px-2 hover:text-secondary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring-primary"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
aria-label={isOpen ? 'Close dropdown' : 'Open dropdown'}
|
||||
>
|
||||
@@ -127,7 +127,7 @@ const InputWithDropdown = React.forwardRef<HTMLInputElement, InputWithDropdownPr
|
||||
'cursor-pointer rounded-md px-3 py-2',
|
||||
'focus:bg-surface-tertiary focus:outline-none focus:ring-1 focus:ring-inset focus:ring-ring-primary',
|
||||
index === highlightedIndex
|
||||
? 'text-primary bg-surface-active'
|
||||
? 'bg-surface-active text-primary'
|
||||
: 'text-secondary hover:bg-surface-tertiary',
|
||||
)}
|
||||
onClick={() => handleSelect(option)}
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
|
||||
import { cn } from '../../utils';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
@@ -53,7 +53,7 @@ export default function MultiSearch({
|
||||
<button
|
||||
className={cn(
|
||||
'relative flex h-5 w-5 items-center justify-end rounded-md text-text-secondary-alt',
|
||||
value?.length ?? 0 ? 'cursor-pointer opacity-100' : 'hidden',
|
||||
(value?.length ?? 0) ? 'cursor-pointer opacity-100' : 'hidden',
|
||||
)}
|
||||
aria-label={'Clear search'}
|
||||
onClick={clearSearch}
|
||||
@@ -63,7 +63,7 @@ export default function MultiSearch({
|
||||
aria-hidden={'true'}
|
||||
className={cn(
|
||||
'text-text-secondary-alt',
|
||||
value?.length ?? 0 ? 'cursor-pointer opacity-100' : 'opacity-0',
|
||||
(value?.length ?? 0) ? 'cursor-pointer opacity-100' : 'opacity-0',
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from './OriginalDialog';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { Button } from './Button';
|
||||
import { Spinner } from '../svg';
|
||||
import { Spinner } from '~/svgs';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
type SelectionProps = {
|
||||
@@ -1,4 +1,5 @@
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export const QuestionMark = ({ className = '' }) => {
|
||||
return (
|
||||
<span>
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
ListboxOptions,
|
||||
} from '@headlessui/react';
|
||||
import type { Option, OptionWithIcon, DropdownValueSetter } from '~/common';
|
||||
import CheckMark from '~/components/svg/CheckMark';
|
||||
import { useMultiSearch } from './MultiSearch';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils/';
|
||||
import { CheckMark } from '~/svgs';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
type SelectDropDownProps = {
|
||||
id?: string;
|
||||
26
packages/client/src/components/Slider.tsx
Normal file
26
packages/client/src/components/Slider.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import * as SliderPrimitive from '@radix-ui/react-slider';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & { onDoubleClick?: () => void }
|
||||
>(({ className, onDoubleClick, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex w-full cursor-pointer touch-none select-none items-center',
|
||||
className,
|
||||
)}
|
||||
onDoubleClick={onDoubleClick}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { Slider };
|
||||
@@ -1,6 +1,36 @@
|
||||
import { useSprings, animated, SpringConfig } from '@react-spring/web';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface SegmenterOptions {
|
||||
granularity?: 'grapheme' | 'word' | 'sentence';
|
||||
localeMatcher?: 'lookup' | 'best fit';
|
||||
}
|
||||
|
||||
interface SegmentData {
|
||||
segment: string;
|
||||
index: number;
|
||||
input: string;
|
||||
isWordLike?: boolean;
|
||||
}
|
||||
|
||||
interface Segments {
|
||||
[Symbol.iterator](): IterableIterator<SegmentData>;
|
||||
}
|
||||
|
||||
interface IntlSegmenter {
|
||||
segment(input: string): Segments;
|
||||
}
|
||||
|
||||
interface IntlSegmenterConstructor {
|
||||
new (locales?: string | string[], options?: SegmenterOptions): IntlSegmenter;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Intl {
|
||||
Segmenter: IntlSegmenterConstructor;
|
||||
}
|
||||
}
|
||||
|
||||
interface SplitTextProps {
|
||||
text?: string;
|
||||
className?: string;
|
||||
@@ -16,12 +46,14 @@ interface SplitTextProps {
|
||||
}
|
||||
|
||||
const splitGraphemes = (text: string): string[] => {
|
||||
if (typeof Intl !== 'undefined' && Intl.Segmenter) {
|
||||
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
||||
if (typeof Intl !== 'undefined' && 'Segmenter' in Intl) {
|
||||
const segmenter = new (Intl as typeof Intl & { Segmenter: IntlSegmenterConstructor }).Segmenter(
|
||||
'en',
|
||||
{ granularity: 'grapheme' },
|
||||
);
|
||||
const segments = segmenter.segment(text);
|
||||
return Array.from(segments).map((s) => s.segment);
|
||||
return Array.from(segments).map((s: SegmentData) => s.segment);
|
||||
} else {
|
||||
// Fallback for browsers without Intl.Segmenter
|
||||
return [...text];
|
||||
}
|
||||
};
|
||||
@@ -45,23 +77,20 @@ const SplitText: React.FC<SplitTextProps> = ({
|
||||
const ref = useRef<HTMLParagraphElement>(null);
|
||||
const animatedCount = useRef(0);
|
||||
|
||||
const springs = useSprings(
|
||||
letters.length,
|
||||
letters.map((_, i) => ({
|
||||
from: animationFrom,
|
||||
to: inView
|
||||
? async (next: (props: any) => Promise<void>) => {
|
||||
await next(animationTo);
|
||||
animatedCount.current += 1;
|
||||
if (animatedCount.current === letters.length && onLetterAnimationComplete) {
|
||||
onLetterAnimationComplete();
|
||||
}
|
||||
const springs = useSprings(letters.length, (i) => ({
|
||||
from: animationFrom,
|
||||
to: inView
|
||||
? async (next) => {
|
||||
await next(animationTo);
|
||||
animatedCount.current += 1;
|
||||
if (animatedCount.current === letters.length && onLetterAnimationComplete) {
|
||||
onLetterAnimationComplete();
|
||||
}
|
||||
: animationFrom,
|
||||
delay: i * delay,
|
||||
config: { easing },
|
||||
})),
|
||||
);
|
||||
}
|
||||
: animationFrom,
|
||||
delay: i * delay,
|
||||
config: { easing },
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
@@ -30,17 +30,17 @@ const TagPrimitiveRoot = React.forwardRef<HTMLDivElement, TagProps>(
|
||||
{CancelButton
|
||||
? CancelButton
|
||||
: onRemove && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemove(e);
|
||||
}}
|
||||
className="rounded-full bg-green-600/50"
|
||||
aria-label={`Remove ${label}`}
|
||||
>
|
||||
<X className="m-[1.5px] p-1" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemove(e);
|
||||
}}
|
||||
className="rounded-full bg-green-600/50"
|
||||
aria-label={`Remove ${label}`}
|
||||
>
|
||||
<X className="m-[1.5px] p-1" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
@@ -1,8 +1,7 @@
|
||||
/* eslint-disable */
|
||||
import * as React from 'react';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
|
||||
import { cn } from '../../utils';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { forwardRef, useLayoutEffect, useState } from 'react';
|
||||
import ReactTextareaAutosize from 'react-textarea-autosize';
|
||||
import type { TextareaAutosizeProps } from 'react-textarea-autosize';
|
||||
import store from '~/store';
|
||||
import { chatDirectionAtom } from '~/store';
|
||||
|
||||
export const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
|
||||
(props, ref) => {
|
||||
const [, setIsRerendered] = useState(false);
|
||||
const chatDirection = useRecoilValue(store.chatDirection).toLowerCase();
|
||||
const chatDirection = useAtomValue(chatDirectionAtom).toLowerCase();
|
||||
useLayoutEffect(() => setIsRerendered(true), []);
|
||||
return <ReactTextareaAutosize dir={chatDirection} {...props} ref={ref} />;
|
||||
},
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useContext, useCallback, useEffect, useState } from 'react';
|
||||
import { useContext, useCallback, useEffect, useState } from 'react';
|
||||
import { Sun, Moon, Monitor } from 'lucide-react';
|
||||
import { ThemeContext } from '~/hooks';
|
||||
|
||||
@@ -8,8 +8,10 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
type ThemeType = 'system' | 'dark' | 'light';
|
||||
|
||||
const Theme = ({ theme, onChange }: { theme: string; onChange: (value: string) => void }) => {
|
||||
const themeIcons = {
|
||||
const themeIcons: Record<ThemeType, JSX.Element> = {
|
||||
system: <Monitor />,
|
||||
dark: <Moon color="white" />,
|
||||
light: <Sun />,
|
||||
@@ -45,7 +47,7 @@ const Theme = ({ theme, onChange }: { theme: string; onChange: (value: string) =
|
||||
}
|
||||
}}
|
||||
>
|
||||
{themeIcons[theme]}
|
||||
{themeIcons[theme as ThemeType]}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -38,10 +38,7 @@ export { default as DropdownPopup } from './DropdownPopup';
|
||||
export { default as DelayedRender } from './DelayedRender';
|
||||
export { default as ThemeSelector } from './ThemeSelector';
|
||||
export { default as SelectDropDown } from './SelectDropDown';
|
||||
export { default as MultiSelectPop } from './MultiSelectPop';
|
||||
export { default as ModelParameters } from './ModelParameters';
|
||||
export { default as OGDialogTemplate } from './OGDialogTemplate';
|
||||
export { default as InputWithDropdown } from './InputWithDropDown';
|
||||
export { default as SelectDropDownPop } from './SelectDropDownPop';
|
||||
export { default as AnimatedSearchInput } from './AnimatedSearchInput';
|
||||
export { default as MultiSelectDropDown } from './MultiSelectDropDown';
|
||||
@@ -1,9 +1,9 @@
|
||||
//ThemeContext.js
|
||||
// source: https://plainenglish.io/blog/light-and-dark-mode-in-react-web-application-with-tailwind-css-89674496b942
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import React, { createContext, useState, useEffect } from 'react';
|
||||
import { getInitialTheme, applyFontSize } from '~/utils';
|
||||
import store from '~/store';
|
||||
import { fontSizeAtom } from '~/store';
|
||||
|
||||
type ProviderValue = {
|
||||
theme: string;
|
||||
@@ -26,9 +26,15 @@ export const isDark = (theme: string): boolean => {
|
||||
|
||||
export const ThemeContext = createContext<ProviderValue>(defaultContextValue);
|
||||
|
||||
export const ThemeProvider = ({ initialTheme, children }) => {
|
||||
export const ThemeProvider = ({
|
||||
initialTheme,
|
||||
children,
|
||||
}: {
|
||||
initialTheme?: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const [theme, setTheme] = useState(getInitialTheme);
|
||||
const setFontSize = useSetRecoilState(store.fontSize);
|
||||
const setFontSize = useSetAtom(fontSizeAtom);
|
||||
|
||||
const rawSetTheme = (rawTheme: string) => {
|
||||
const root = window.document.documentElement;
|
||||
10
packages/client/src/hooks/index.ts
Normal file
10
packages/client/src/hooks/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export * from './ThemeContext';
|
||||
|
||||
export type { TranslationKeys } from './useLocalize';
|
||||
|
||||
export { default as useToast } from './useToast';
|
||||
export { default as useCombobox } from './useCombobox';
|
||||
export { default as useLocalize } from './useLocalize';
|
||||
export { default as useMediaQuery } from './useMediaQuery';
|
||||
export { default as useDelayedRender } from './useDelayedRender';
|
||||
export { default as useOnClickOutside } from './useOnClickOutside';
|
||||
21
packages/client/src/hooks/useLocalize.ts
Normal file
21
packages/client/src/hooks/useLocalize.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { useEffect } from 'react';
|
||||
import { TOptions } from 'i18next';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { resources } from '~/locales/i18n';
|
||||
import { langAtom } from '~/store';
|
||||
|
||||
export type TranslationKeys = keyof typeof resources.en.translation;
|
||||
|
||||
export default function useLocalize() {
|
||||
const lang = useAtomValue(langAtom);
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (i18n.language !== lang) {
|
||||
i18n.changeLanguage(lang);
|
||||
}
|
||||
}, [lang, i18n]);
|
||||
|
||||
return (phraseKey: TranslationKeys, options?: TOptions) => t(phraseKey, options);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user