diff --git a/web/src/app.tsx b/web/src/app.tsx index 8bd234a0e..7a2086440 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -1,16 +1,8 @@ import { Toaster as Sonner } from '@/components/ui/sonner'; import { Toaster } from '@/components/ui/toaster'; -import i18n from '@/locales/config'; +import i18n, { changeLanguageAsync } from '@/locales/config'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { configResponsive } from 'ahooks'; -import { App, ConfigProvider, ConfigProviderProps, theme } from 'antd'; -import pt_BR from 'antd/lib/locale/pt_BR'; -import deDE from 'antd/locale/de_DE'; -import enUS from 'antd/locale/en_US'; -import ru_RU from 'antd/locale/ru_RU'; -import vi_VN from 'antd/locale/vi_VN'; -import zhCN from 'antd/locale/zh_CN'; -import zh_HK from 'antd/locale/zh_HK'; import dayjs from 'dayjs'; import advancedFormat from 'dayjs/plugin/advancedFormat'; import customParseFormat from 'dayjs/plugin/customParseFormat'; @@ -18,13 +10,12 @@ import localeData from 'dayjs/plugin/localeData'; import weekOfYear from 'dayjs/plugin/weekOfYear'; import weekYear from 'dayjs/plugin/weekYear'; import weekday from 'dayjs/plugin/weekday'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { RouterProvider } from 'react-router'; -import { ThemeProvider, useTheme } from './components/theme-provider'; +import { ThemeProvider } from './components/theme-provider'; import { SidebarProvider } from './components/ui/sidebar'; import { TooltipProvider } from './components/ui/tooltip'; import { ThemeEnum } from './constants/common'; -// import { getRouter } from './routes'; import { routers } from './routes'; import storage from './utils/authorization-util'; @@ -47,24 +38,6 @@ dayjs.extend(localeData); dayjs.extend(weekOfYear); dayjs.extend(weekYear); -const AntLanguageMap = { - en: enUS, - zh: zhCN, - 'zh-TRADITIONAL': zh_HK, - ru: ru_RU, - vi: vi_VN, - 'pt-BR': pt_BR, - de: deDE, -}; - -// if (process.env.NODE_ENV === 'development') { -// const whyDidYouRender = require('@welldone-software/why-did-you-render'); -// whyDidYouRender(React, { -// trackAllPureComponents: true, -// trackExtraHooks: [], -// logOnDifferentValues: true, -// }); -// } if (process.env.NODE_ENV === 'development') { import('@welldone-software/why-did-you-render').then( (whyDidYouRenderModule) => { @@ -78,6 +51,7 @@ if (process.env.NODE_ENV === 'development') { }, ); } + const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -87,53 +61,31 @@ const queryClient = new QueryClient({ }, }); -type Locale = ConfigProviderProps['locale']; - function Root({ children }: React.PropsWithChildren) { - const { theme: themeragflow } = useTheme(); - const getLocale = (lng: string) => - AntLanguageMap[lng as keyof typeof AntLanguageMap] ?? enUS; - - const [locale, setLocal] = useState(getLocale(storage.getLanguage())); + useEffect(() => { + const lng = storage.getLanguage(); + if (lng) { + document.documentElement.lang = lng; + } + }, []); i18n.on('languageChanged', function (lng: string) { storage.setLanguage(lng); - setLocal(getLocale(lng)); - // Should reflect to document.documentElement.lang = lng; }); return ( - <> - - - {children} - - - - - {/* */} - + +
{children}
+
); } const RootProvider = ({ children }: React.PropsWithChildren) => { useEffect(() => { - // Because the language is saved in the backend, a token is required to obtain the api. However, the login page cannot obtain the language through the getUserInfo api, so the language needs to be saved in localstorage. const lng = storage.getLanguage(); if (lng) { - i18n.changeLanguage(lng); + changeLanguageAsync(lng); } }, []); @@ -145,6 +97,8 @@ const RootProvider = ({ children }: React.PropsWithChildren) => { storageKey="ragflow-ui-theme" > {children} + + @@ -159,16 +113,6 @@ const RouterProviderWrapper: React.FC<{ router: typeof routers }> = ({ RouterProviderWrapper.whyDidYouRender = false; export default function AppContainer() { - // const [router, setRouter] = useState(null); - - // useEffect(() => { - // getRouter().then(setRouter); - // }, []); - - // if (!router) { - // return
Loading...
; - // } - return ( diff --git a/web/src/components/document-preview/hooks.ts b/web/src/components/document-preview/hooks.ts index 097185b24..6ecdf3815 100644 --- a/web/src/components/document-preview/hooks.ts +++ b/web/src/components/document-preview/hooks.ts @@ -165,3 +165,24 @@ export const useFetchDocx = (filePath: string) => { return { succeed, containerRef, error }; }; + +export const useCatchDocumentError = (url: string) => { + const httpHeaders = useMemo(() => { + return { + [Authorization]: getAuthorization(), + }; + }, []); + const [error, setError] = useState(''); + + const fetchDocument = useCallback(async () => { + const { data } = await axios.get(url, { headers: httpHeaders }); + if (data.code !== 0) { + setError(data?.message); + } + }, [url, httpHeaders]); + useEffect(() => { + fetchDocument(); + }, [fetchDocument]); + + return error; +}; diff --git a/web/src/components/document-preview/pdf-preview.tsx b/web/src/components/document-preview/pdf-preview.tsx index babcd37ef..d8fb31c97 100644 --- a/web/src/components/document-preview/pdf-preview.tsx +++ b/web/src/components/document-preview/pdf-preview.tsx @@ -13,7 +13,7 @@ import { Spin } from '@/components/ui/spin'; import { Authorization } from '@/constants/authorization'; import FileError from '@/pages/document-viewer/file-error'; import { getAuthorization } from '@/utils/authorization-util'; -import { useCatchDocumentError } from '../pdf-previewer/hooks'; +import { useCatchDocumentError } from './hooks'; import styles from './index.module.less'; type PdfLoaderProps = React.ComponentProps & { httpHeaders?: Record; diff --git a/web/src/components/floating-chat-widget.tsx b/web/src/components/floating-chat-widget.tsx index 14b85018d..c0bd169aa 100644 --- a/web/src/components/floating-chat-widget.tsx +++ b/web/src/components/floating-chat-widget.tsx @@ -3,7 +3,7 @@ import { useClickDrawer } from '@/components/pdf-drawer/hooks'; import { MessageType, SharedFrom } from '@/constants/chat'; import { useFetchExternalAgentInputs } from '@/hooks/use-agent-request'; import { useFetchExternalChatInfo } from '@/hooks/use-chat-request'; -import i18n from '@/locales/config'; +import i18n, { changeLanguageAsync } from '@/locales/config'; import { useSendNextSharedMessage } from '@/pages/agent/hooks/use-send-shared-message'; import { MessageCircle, Minimize2, Send, X } from 'lucide-react'; import React, { useCallback, useEffect, useRef, useState } from 'react'; @@ -136,7 +136,7 @@ const FloatingChatWidget = () => { }, 50); if (locale && i18n.language !== locale) { - i18n.changeLanguage(locale); + changeLanguageAsync(locale); } return () => clearTimeout(timer); diff --git a/web/src/components/message-item/group-button.tsx b/web/src/components/message-item/group-button.tsx index aad19cb40..58b451f26 100644 --- a/web/src/components/message-item/group-button.tsx +++ b/web/src/components/message-item/group-button.tsx @@ -1,5 +1,11 @@ import { PromptIcon } from '@/assets/icon/next-icon'; import CopyToClipboard from '@/components/copy-to-clipboard'; +import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; import { useSetModalState } from '@/hooks/common-hooks'; import { IRemoveMessageById } from '@/hooks/logic-hooks'; import { @@ -10,7 +16,6 @@ import { SoundOutlined, SyncOutlined, } from '@ant-design/icons'; -import { Radio, Tooltip } from 'antd'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import FeedbackDialog from '../feedback-dialog'; @@ -50,34 +55,44 @@ export const AssistantGroupButton = ({ return ( <> - - + + - + {showLoudspeaker && ( - - - {isPlaying ? : } + + + + + {isPlaying ? : } + + + {t('chat.read')} - + )} {showLikeButton && ( <> - + - - + + - + )} {prompt && ( - + - + )} - + {visible && ( - + + - + {regenerateMessage && ( - - - + + + + + {t('chat.regenerate')} - + )} {removeMessageById && ( - - - + + + + + + {t('common.delete')} - + )} - + ); }; diff --git a/web/src/components/next-message-item/group-button.tsx b/web/src/components/next-message-item/group-button.tsx index 1719511de..6c0620f75 100644 --- a/web/src/components/next-message-item/group-button.tsx +++ b/web/src/components/next-message-item/group-button.tsx @@ -1,5 +1,10 @@ import { PromptIcon } from '@/assets/icon/next-icon'; import CopyToClipboard from '@/components/copy-to-clipboard'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; import { useSetModalState } from '@/hooks/common-hooks'; import { IRemoveMessageById } from '@/hooks/logic-hooks'; import { AgentChatContext } from '@/pages/agent/context'; @@ -13,7 +18,6 @@ import { SoundOutlined, SyncOutlined, } from '@ant-design/icons'; -import { Radio, Tooltip } from 'antd'; import { Download, NotebookText } from 'lucide-react'; import { useCallback, useContext } from 'react'; import { useTranslation } from 'react-i18next'; @@ -80,8 +84,13 @@ export const AssistantGroupButton = ({ {showLoudspeaker && ( - - {isPlaying ? : } + + + + {isPlaying ? : } + + + {t('chat.read')} @@ -97,9 +106,9 @@ export const AssistantGroupButton = ({ )} {prompt && ( - + - + )} {showLog && ( @@ -168,28 +177,39 @@ export const UserGroupButton = ({ const { t } = useTranslation(); return ( - - + + - + {regenerateMessage && ( - - - + + + + + {t('chat.regenerate')} - + )} {removeMessageById && ( - - - + + + + + + {t('common.delete')} - + )} - + ); }; diff --git a/web/src/components/parse-configuration/index.tsx b/web/src/components/parse-configuration/index.tsx deleted file mode 100644 index 446412bf4..000000000 --- a/web/src/components/parse-configuration/index.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { DocumentParserType } from '@/constants/knowledge'; -import { useTranslate } from '@/hooks/common-hooks'; -import { PlusOutlined } from '@ant-design/icons'; -import { Button, Flex, Form, Input, InputNumber, Slider, Switch } from 'antd'; -import random from 'lodash/random'; - -export const excludedParseMethods = [ - DocumentParserType.Table, - DocumentParserType.Resume, - DocumentParserType.One, - DocumentParserType.Picture, - DocumentParserType.KnowledgeGraph, - DocumentParserType.Qa, - DocumentParserType.Tag, -]; - -export const showRaptorParseConfiguration = ( - parserId: DocumentParserType | undefined, -) => { - return !excludedParseMethods.some((x) => x === parserId); -}; - -export const excludedTagParseMethods = [ - DocumentParserType.Table, - DocumentParserType.KnowledgeGraph, - DocumentParserType.Tag, -]; - -export const showTagItems = (parserId: DocumentParserType) => { - return !excludedTagParseMethods.includes(parserId); -}; - -// The three types "table", "resume" and "one" do not display this configuration. -const ParseConfiguration = () => { - const form = Form.useFormInstance(); - const { t } = useTranslate('knowledgeConfiguration'); - - const handleGenerate = () => { - form.setFieldValue( - ['parser_config', 'raptor', 'random_seed'], - random(10000), - ); - }; - - return ( - <> - - - - - prevValues.parser_config.raptor.use_raptor !== - curValues.parser_config.raptor.use_raptor - } - > - {({ getFieldValue }) => { - const useRaptor = getFieldValue([ - 'parser_config', - 'raptor', - 'use_raptor', - ]); - - return ( - useRaptor && ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - ); - }} - - - ); -}; - -export default ParseConfiguration; diff --git a/web/src/components/parse-configuration/raptor-form-fields-old.tsx b/web/src/components/parse-configuration/raptor-form-fields-old.tsx deleted file mode 100644 index 28cd79ced..000000000 --- a/web/src/components/parse-configuration/raptor-form-fields-old.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { DocumentParserType } from '@/constants/knowledge'; -import { useTranslate } from '@/hooks/common-hooks'; -import random from 'lodash/random'; -import { Plus } from 'lucide-react'; -import { useCallback } from 'react'; -import { useFormContext, useWatch } from 'react-hook-form'; -import { SliderInputFormField } from '../slider-input-form-field'; -import { Button } from '../ui/button'; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from '../ui/form'; -import { Input } from '../ui/input'; -import { Switch } from '../ui/switch'; -import { Textarea } from '../ui/textarea'; - -export const excludedParseMethods = [ - DocumentParserType.Table, - DocumentParserType.Resume, - DocumentParserType.One, - DocumentParserType.Picture, - DocumentParserType.KnowledgeGraph, - DocumentParserType.Qa, - DocumentParserType.Tag, -]; - -export const showRaptorParseConfiguration = ( - parserId: DocumentParserType | undefined, -) => { - return !excludedParseMethods.some((x) => x === parserId); -}; - -export const excludedTagParseMethods = [ - DocumentParserType.Table, - DocumentParserType.KnowledgeGraph, - DocumentParserType.Tag, -]; - -export const showTagItems = (parserId: DocumentParserType) => { - return !excludedTagParseMethods.includes(parserId); -}; - -const UseRaptorField = 'parser_config.raptor.use_raptor'; -const RandomSeedField = 'parser_config.raptor.random_seed'; - -// The three types "table", "resume" and "one" do not display this configuration. - -const RaptorFormFields = () => { - const form = useFormContext(); - const { t } = useTranslate('knowledgeConfiguration'); - const useRaptor = useWatch({ name: UseRaptorField }); - - const handleGenerate = useCallback(() => { - form.setValue(RandomSeedField, random(10000)); - }, [form]); - - return ( - <> - ( - - {t('useRaptor')} - - - - - - )} - /> - {useRaptor && ( -
- ( - - {t('prompt')} - -