mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 09:50:15 +01:00
📦 feat: Move Shared Components to @librechat/client (#8685)
* feat: init @librechat/client * feat: Add common types and interfaces for accessibility, agents, artifacts, assistants, and tools * feat: Add jotai as a peer dependency * fix build client package * feat: cleanup unused types from common/index.ts - Remove 104 unused type exports from packages/client/src/common/index.ts - Keep only 7 actually used exports (93% reduction) - Add cleanup script with enhanced import pattern detection - Support both named imports and namespace imports (* as t) - Create automatic backups and comprehensive documentation - Maintain type safety with build verification - No breaking changes to existing code Kept exports: - TShowToast, Option, OptionWithIcon, DropdownValueSetter - MentionOption, NotificationSeverity, MenuItemProps Scripts: cleanup-common-types-safe.js, README-CLEANUP.md * fix: cleanup * fix: package; refactor: tsconfig * feat: add back `recoil` * fix: move dependencies to peerDependencies in client package * feat: add @librechat/client as a dependency in package.json and package-lock.json * feat: update client package configuration and dependencies - Added new dependencies for Rollup plugins and updated existing ones in package.json and package-lock.json. - Introduced a new Rollup configuration file for building the client package. - Refactored build scripts to include a dedicated build command for the client. - Updated TypeScript configuration for improved module resolution and type declaration output. - Integrated a Toast component from the client package into the main App component. * feat: enhance Rollup configuration for client package - Updated terser plugin settings to preserve directives like 'use client'. - Added custom warning handler to ignore "use client" directive warnings during the build process. * chore: rename package/client build script command * feat: update client package dependencies and Rollup configuration - Added rollup-plugin-postcss to package.json and updated package-lock.json. - Enhanced Rollup configuration to include postcss plugin for CSS handling. - Updated index.ts to export all components from the components directory for better modularity. * feat: add client package directory to update configuration - Included the 'client' package directory in the update.js configuration to ensure it is recognized during updates. * feat: export Toast component in client package - Added export for the Toast component in index.ts to enhance modularity and accessibility of components. * feat: /client transition to @librechat/client * chore: fixed formatting issues * fix: update peer dependencies in @librechat/client to prevent bundling them * fix: correct useSprings implementation in SplitText component * fix: circular dependencies in DataTable * fix: add remaining peer dependencies and match actual versions previously used in `client/package.json` * fix: correct frontend:ci script to include client package build * chore: enhance unused package detection for @librechat/client and improve dependency extraction * fix: add missing peer dependency for @radix-ui/react-collapsible * chore: include "packages/client" in unused i18next keys detection * test: update AgentFooter tests to use document.querySelector for spinner checks test: mock window.matchMedia in setupTests.js for consistent test environment * feat: add react-hook-form dependency and update FormInput component to use its types * chore: linting * refactor: remove unused defaultSelectedValues prop from MCPSelect and MultiSelect components * chore: linting * feat: update GitHub Actions workflow to publish @librechat/client * chore: update GitHub Actions workflow to install and build data-provider and client dependencies * chore: add missing @testing-library/react dependency to client package * chore: update tsconfig.json to exclude additional test files * chore: fix build issues, resolve latest LC changes * chore: move MCP components outside of `~/components/ui` * feat: implement dynamic theme system with environment variable support and Tailwind CSS integration * chore: remove unnecessary logging of sttExternal and ttsExternal in Speech component * chore: squashed cleanup commits chore: move @tanstack/react-virtual to dependencies and remove recoil from package.json chore: move dependencies to peerDependencies in package.json feat: update package.json and rollup.config.js to include jotai and enhance bundling configuration feat: update package.json and rollup.config.js to include jotai and enhance bundling configuration refactor: reorganize exports in index.ts for improved clarity refactor: remove unused types and interfaces from common files refactor: update peer dependencies and improve component typings - Removed duplicate peer dependencies from package.json and organized them. - Updated rollup.config.js to disable TypeScript checking during the build process. - Modified AnimatedTabs component to use React.ReactNode for label and content types, and added TypeScript workarounds for compatibility. - Enhanced Label and Separator components to accept an optional className prop and improved prop spreading. - Updated Slider component to include an optional className prop and refined prop handling for better type safety. refactor: clean up client workflow and update package dependencies refactor: update package dependencies and improve PostCSS and Rollup configurations chore: bump version to 0.1.2 in package.json chore: bump client version to 0.1.2 in package-lock.json chore: bump client version to 0.1.3 and update dependencies chore: bump client version to 0.1.4 and update @react-spring dependencies chore: update package version to 0.1.5 and adjust peer dependencies - Bump version in package.json from 0.1.4 to 0.1.5. - Update peer dependency for @tanstack/react-query to allow version 5.0.0. - Add @tanstack/react-table and @tanstack/react-virtual as dependencies. - Update various dependencies to their latest compatible versions. - Simplify postcss.config.js by removing unnecessary options. - Clean up rollup.config.js by removing ignored PostCSS warnings. - Update CheckboxButton component to cast icon as React JSX element. - Adjust Combobox component's class names for better styling. - Change DropdownPopup component to use React's namespace import. - Modify InputOTP component to use 'any' type for OTPInputContext. - Ensure displayLabel and value in ModelParameters are converted to strings. - Update MultiSearch component's placeholder to ensure it's a string. - Cast selectIcon in MultiSelect as React JSX element for consistency. - Update OGDialogTemplate to cast selectText as React JSX element. - Initialize animationRef in PixelCard with undefined for clarity. - Add TypeScript ignore comments in Select and SelectDropDown components for Radix UI type conflicts. - Ensure title in SelectDropDown is a string and adjust rendering of options. - Update useLocalize hook to cast options as any for compatibility. refactor: code structure; chore: translations cleanup chore: remove unused imports and clean up code in NewChat component refactor: enhance Menu component to support custom render functions for menu items style: update itemClassName in ToolsDropdown for improved UI consistency fix: merge conflicts chore: update @radix-ui/react-accordion to version 1.2.11 * refactor: remove unnecessary TypeScript type assertions in AnimatedTabs, Label, Separator, and Slider components * feat: enhance theme system with localStorage persistence and new theme atoms * chore: bump version of @librechat/client to 0.1.7 * chore: fix ci/cd warnings/errors related to linting and unused localization keys * chore: update dependencies for class-variance-authority, clsx, and match-sorter * chore: bump @librechat/client to v0.1.8 * feat: add utility colors for theme customization and remove unused tailwindConfig * v0.1.9 --------- Co-authored-by: Marco Beretta <81851188+berry-13@users.noreply.github.com>
This commit is contained in:
parent
97e1cdd224
commit
79197454f8
569 changed files with 7010 additions and 1848 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import filenamify from 'filenamify';
|
||||
import exportFromJSON from 'export-from-json';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { QueryKeys } from 'librechat-data-provider';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
|
|
@ -13,11 +14,11 @@ import {
|
|||
} from '~/data-provider';
|
||||
import { cleanupPreset, removeUnavailableTools, getConvoSwitchLogic } from '~/utils';
|
||||
import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo';
|
||||
import { useChatContext, useToastContext } from '~/Providers';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import useNewConvo from '~/hooks/useNewConvo';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
export default function usePresets() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { IconMapProps, AgentIconMapProps, IconsRecord } from '~/common';
|
||||
import { Feather } from 'lucide-react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import {
|
||||
MinimalPlugin,
|
||||
GPTIcon,
|
||||
|
|
@ -12,7 +11,8 @@ import {
|
|||
LightningIcon,
|
||||
BedrockIcon,
|
||||
Sparkles,
|
||||
} from '~/components/svg';
|
||||
} from '@librechat/client';
|
||||
import type { IconMapProps, AgentIconMapProps, IconsRecord } from '~/common';
|
||||
import UnknownIcon from './UnknownIcon';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { memo } from 'react';
|
||||
import { CustomMinimalIcon, XAIcon } from '@librechat/client';
|
||||
import { EModelEndpoint, KnownEndpoints } from 'librechat-data-provider';
|
||||
import { CustomMinimalIcon, XAIcon } from '~/components/svg';
|
||||
import { IconContext } from '~/common';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
import { useToastContext } from '~/Providers/ToastContext';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export const useDelayedUploadToast = () => {
|
||||
const localize = useLocalize();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { v4 } from 'uuid';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
QueryKeys,
|
||||
|
|
@ -17,7 +18,6 @@ import { useGetFileConfig, useUploadFileMutation } from '~/data-provider';
|
|||
import useLocalize, { TranslationKeys } from '~/hooks/useLocalize';
|
||||
import { useDelayedUploadToast } from './useDelayedUploadToast';
|
||||
import { processFileForUpload } from '~/utils/heicConverter';
|
||||
import { useToastContext } from '~/Providers/ToastContext';
|
||||
import { useChatContext } from '~/Providers/ChatContext';
|
||||
import { logger, validateFiles } from '~/utils';
|
||||
import useClientResize from './useClientResize';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ export * from './useAutoSave';
|
|||
export { default as useUserKey } from './useUserKey';
|
||||
export { default as useDebounce } from './useDebounce';
|
||||
export { default as useTextarea } from './useTextarea';
|
||||
export { default as useCombobox } from './useCombobox';
|
||||
export { default as useQueryParams } from './useQueryParams';
|
||||
export { default as useHandleKeyUp } from './useHandleKeyUp';
|
||||
export { default as useRequiresKey } from './useRequiresKey';
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
import { useMemo, useState } from 'react';
|
||||
import { matchSorter } from 'match-sorter';
|
||||
import type { OptionWithIcon, MentionOption } from '~/common';
|
||||
|
||||
export default function useCombobox({
|
||||
value,
|
||||
options,
|
||||
}: {
|
||||
value: string;
|
||||
options: Array<OptionWithIcon | MentionOption>;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
|
||||
const matches = useMemo(() => {
|
||||
if (!searchValue) {
|
||||
return options;
|
||||
}
|
||||
const keys = ['label', 'value'];
|
||||
const matches = matchSorter(options, searchValue, { keys });
|
||||
// Radix Select does not work if we don't render the selected item, so we
|
||||
// make sure to include it in the list of matches.
|
||||
const selectedItem = options.find((currentItem) => currentItem.value === value);
|
||||
if (selectedItem && !matches.includes(selectedItem)) {
|
||||
matches.push(selectedItem);
|
||||
}
|
||||
return matches;
|
||||
}, [searchValue, value, options]);
|
||||
|
||||
return {
|
||||
open,
|
||||
setOpen,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
matches,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect, useRef, useMemo } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognition';
|
||||
import useGetAudioSettings from './useGetAudioSettings';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import store from '~/store';
|
||||
|
||||
const useSpeechToTextBrowser = (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { useSpeechToTextMutation } from '~/data-provider';
|
||||
import useGetAudioSettings from './useGetAudioSettings';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import store from '~/store';
|
||||
|
||||
const useSpeechToTextExternal = (
|
||||
|
|
@ -107,7 +107,7 @@ const useSpeechToTextExternal = (
|
|||
});
|
||||
setPermission(true);
|
||||
audioStream.current = streamData ?? null;
|
||||
} catch (err) {
|
||||
} catch {
|
||||
setPermission(false);
|
||||
}
|
||||
};
|
||||
|
|
@ -268,6 +268,7 @@ const useSpeechToTextExternal = (
|
|||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isListening]);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import { useState, useMemo, useRef, useCallback, useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { useTextToSpeechMutation, useVoicesQuery } from '~/data-provider';
|
||||
import { useToastContext } from '~/Providers/ToastContext';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
const createFormData = (text: string, voice: string) => {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import useGetSender from '~/hooks/Conversations/useGetSender';
|
|||
import useFileHandling from '~/hooks/Files/useFileHandling';
|
||||
import { useInteractionHealthCheck } from '~/data-provider';
|
||||
import { useChatContext } from '~/Providers/ChatContext';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { globalAudioId } from '~/common';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
type KeyEvent = KeyboardEvent<HTMLTextAreaElement>;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useCallback, useState, useEffect, useMemo } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { QueryKeys } from 'librechat-data-provider';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
useReinitializeMCPServerMutation,
|
||||
useCancelMCPOAuthMutation,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import { useMCPConnectionStatusQuery } from '~/data-provider/Tools/queries';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { logger } from '~/utils';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useCallback, useState, useMemo, useRef } from 'react';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { Constants, QueryKeys } from 'librechat-data-provider';
|
||||
import { useCallback, useState, useMemo, useRef } from 'react';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import { useMCPServerInitialization } from '~/hooks/MCP/useMCPServerInitialization';
|
||||
import type { ConfigFieldDetail } from '~/components/ui/MCP/MCPConfigDialog';
|
||||
import type { TUpdateUserPlugins, TPlugin } from 'librechat-data-provider';
|
||||
import { useToastContext, useBadgeRowContext } from '~/Providers';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import type { ConfigFieldDetail } from '~/components/MCP/MCPConfigDialog';
|
||||
import { useLocalize, useMCPServerInitialization } from '~/hooks';
|
||||
import { useBadgeRowContext } from '~/Providers';
|
||||
|
||||
export function useMCPServerManager() {
|
||||
const localize = useLocalize();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useMemo } from 'react';
|
||||
import { Blocks, MCPIcon, AttachmentIcon } from '@librechat/client';
|
||||
import { MessageSquareQuote, ArrowRightToLine, Settings2, Database, Bookmark } from 'lucide-react';
|
||||
import {
|
||||
isAssistantsEndpoint,
|
||||
|
|
@ -15,7 +16,6 @@ import BookmarkPanel from '~/components/SidePanel/Bookmarks/BookmarkPanel';
|
|||
import MemoryViewer from '~/components/SidePanel/Memories/MemoryViewer';
|
||||
import PanelSwitch from '~/components/SidePanel/Builder/PanelSwitch';
|
||||
import PromptsAccordion from '~/components/Prompts/PromptsAccordion';
|
||||
import { Blocks, MCPIcon, AttachmentIcon } from '~/components/svg';
|
||||
import Parameters from '~/components/SidePanel/Parameters/Panel';
|
||||
import FilesPanel from '~/components/SidePanel/Files/Panel';
|
||||
import MCPPanel from '~/components/SidePanel/MCP/MCPPanel';
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ import { useCallback } from 'react';
|
|||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { AuthType, Tools, QueryKeys } from 'librechat-data-provider';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
// import { useToastContext } from '~/Providers';
|
||||
|
||||
const useAuthCodeTool = (options?: { isEntityTool: boolean }) => {
|
||||
// const { showToast } = useToastContext();
|
||||
const queryClient = useQueryClient();
|
||||
const isEntityTool = options?.isEntityTool ?? true;
|
||||
const updateUserPlugins = useUpdateUserPluginsMutation({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useGetCategories } from '~/data-provider';
|
||||
import CategoryIcon from '~/components/Prompts/Groups/CategoryIcon';
|
||||
import useLocalize, { TranslationKeys } from '~/hooks/useLocalize';
|
||||
import { useLocalize, TranslationKeys } from '~/hooks';
|
||||
|
||||
const loadingCategories: { label: TranslationKeys; value: string }[] = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createContext, useRef, useContext, RefObject } from 'react';
|
||||
import { toCanvas } from 'html-to-image';
|
||||
import { ThemeContext } from '~/hooks/ThemeContext';
|
||||
import { ThemeContext } from '@librechat/client';
|
||||
|
||||
type ScreenshotContextType = {
|
||||
ref?: RefObject<HTMLDivElement>;
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
//ThemeContext.js
|
||||
// source: https://plainenglish.io/blog/light-and-dark-mode-in-react-web-application-with-tailwind-css-89674496b942
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import React, { createContext, useState, useEffect } from 'react';
|
||||
import { getInitialTheme, applyFontSize } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
type ProviderValue = {
|
||||
theme: string;
|
||||
setTheme: React.Dispatch<React.SetStateAction<string>>;
|
||||
};
|
||||
|
||||
const defaultContextValue: ProviderValue = {
|
||||
theme: getInitialTheme(),
|
||||
setTheme: () => {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
export const isDark = (theme: string): boolean => {
|
||||
if (theme === 'system') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return theme === 'dark';
|
||||
};
|
||||
|
||||
export const ThemeContext = createContext<ProviderValue>(defaultContextValue);
|
||||
|
||||
export const ThemeProvider = ({ initialTheme, children }) => {
|
||||
const [theme, setTheme] = useState(getInitialTheme);
|
||||
const setFontSize = useSetRecoilState(store.fontSize);
|
||||
|
||||
const rawSetTheme = (rawTheme: string) => {
|
||||
const root = window.document.documentElement;
|
||||
const darkMode = isDark(rawTheme);
|
||||
|
||||
root.classList.remove(darkMode ? 'light' : 'dark');
|
||||
root.classList.add(darkMode ? 'dark' : 'light');
|
||||
|
||||
localStorage.setItem('color-theme', rawTheme);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const changeThemeOnSystemChange = () => {
|
||||
rawSetTheme(mediaQuery.matches ? 'dark' : 'light');
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener('change', changeThemeOnSystemChange);
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', changeThemeOnSystemChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const fontSize = localStorage.getItem('fontSize');
|
||||
if (fontSize == null) {
|
||||
setFontSize('text-base');
|
||||
applyFontSize('text-base');
|
||||
localStorage.setItem('fontSize', JSON.stringify('text-base'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
applyFontSize(JSON.parse(fontSize));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
// Reason: This effect should only run once, and `setFontSize` is a stable function
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (initialTheme) {
|
||||
rawSetTheme(initialTheme);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
rawSetTheme(theme);
|
||||
}, [theme]);
|
||||
|
||||
return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>;
|
||||
};
|
||||
|
|
@ -8,30 +8,26 @@ export * from './Nav';
|
|||
export * from './Files';
|
||||
export * from './Generic';
|
||||
export * from './Input';
|
||||
export * from './MCP';
|
||||
export * from './Messages';
|
||||
export * from './Plugins';
|
||||
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,37 +0,0 @@
|
|||
import { useState, useRef, useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const useDelayedRender = (delay: number) => {
|
||||
const [delayed, setDelayed] = useState(true);
|
||||
const timerPromiseRef = useRef<Promise<void> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (delayed) {
|
||||
const timerPromise = new Promise<void>((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
setDelayed(false);
|
||||
resolve();
|
||||
}, delay);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
});
|
||||
|
||||
timerPromiseRef.current = timerPromise;
|
||||
}
|
||||
|
||||
return () => {
|
||||
timerPromiseRef.current = null;
|
||||
};
|
||||
}, [delay, delayed]);
|
||||
|
||||
return (fn: () => ReactNode) => {
|
||||
if (delayed && timerPromiseRef.current) {
|
||||
throw timerPromiseRef.current;
|
||||
}
|
||||
return fn();
|
||||
};
|
||||
};
|
||||
|
||||
export default useDelayedRender;
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function useMediaQuery(query: string) {
|
||||
const [matches, setMatches] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const media = window.matchMedia(query);
|
||||
if (media.matches !== matches) {
|
||||
setMatches(media.matches);
|
||||
}
|
||||
const listener = () => setMatches(media.matches);
|
||||
media.addEventListener('change', listener);
|
||||
return () => media.removeEventListener('change', listener);
|
||||
}, [matches, query]);
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import { useEffect, RefObject } from 'react';
|
||||
type Handler = () => void;
|
||||
|
||||
export default function useOnClickOutside(
|
||||
ref: RefObject<HTMLElement>,
|
||||
handler: Handler,
|
||||
excludeIds: string[],
|
||||
customCondition?: (target: EventTarget | Element | null) => boolean,
|
||||
): void {
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
const target = event.target as Node | null;
|
||||
|
||||
if (target && 'id' in target && excludeIds.includes((target as HTMLElement).id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
target?.parentNode &&
|
||||
'id' in target.parentNode &&
|
||||
excludeIds.includes((target.parentNode as HTMLElement).id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (customCondition && customCondition(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ref.current && !ref.current.contains(target)) {
|
||||
handler();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ref, handler]);
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import { useRef, useEffect } from 'react';
|
||||
import type { TShowToast } from '~/common';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import store from '~/store';
|
||||
|
||||
export default function useToast(showDelay = 100) {
|
||||
const [toast, setToast] = useRecoilState(store.toastState);
|
||||
const showTimerRef = useRef<number | null>(null);
|
||||
const hideTimerRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (showTimerRef.current !== null) {
|
||||
clearTimeout(showTimerRef.current);
|
||||
}
|
||||
if (hideTimerRef.current !== null) {
|
||||
clearTimeout(hideTimerRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const showToast = ({
|
||||
message,
|
||||
severity = NotificationSeverity.SUCCESS,
|
||||
showIcon = true,
|
||||
duration = 3000, // default duration for the toast to be visible
|
||||
status,
|
||||
}: TShowToast) => {
|
||||
// Clear existing timeouts
|
||||
if (showTimerRef.current !== null) {
|
||||
clearTimeout(showTimerRef.current);
|
||||
}
|
||||
if (hideTimerRef.current !== null) {
|
||||
clearTimeout(hideTimerRef.current);
|
||||
}
|
||||
|
||||
// Timeout to show the toast
|
||||
showTimerRef.current = window.setTimeout(() => {
|
||||
setToast({
|
||||
open: true,
|
||||
message,
|
||||
severity: (status as NotificationSeverity) ?? severity,
|
||||
showIcon,
|
||||
});
|
||||
// Hides the toast after the specified duration
|
||||
hideTimerRef.current = window.setTimeout(() => {
|
||||
setToast((prevToast) => ({ ...prevToast, open: false }));
|
||||
}, duration);
|
||||
}, showDelay);
|
||||
};
|
||||
|
||||
return {
|
||||
toast,
|
||||
onOpenChange: (open: boolean) => setToast({ ...toast, open }),
|
||||
showToast,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue