Theme switch support (#3568)

### What problem does this PR solve?
- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: Yingfeng <yingfeng.zhang@gmail.com>
Co-authored-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
so95 2024-12-10 10:42:04 +07:00 committed by GitHub
parent 7d4f1c0645
commit d5a322a352
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
85 changed files with 1041 additions and 520 deletions

4
.gitignore vendored
View File

@ -35,4 +35,6 @@ rag/res/deepdoc
sdk/python/ragflow.egg-info/
sdk/python/build/
sdk/python/dist/
sdk/python/ragflow_sdk.egg-info/
sdk/python/ragflow_sdk.egg-info/
huggingface.co/
nltk_data/

View File

@ -74,7 +74,7 @@ services:
command:
--max_connections=1000
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--collation-server=utf8mb4_unicode_ci
--default-authentication-plugin=mysql_native_password
--tls_version="TLSv1.2,TLSv1.3"
--init-file /data/application/init.sql

View File

@ -260,7 +260,7 @@ def build_chunks(task, progress_callback):
def init_kb(row, vector_size: int):
idxnm = search.index_name(row["tenant_id"])
return settings.docStoreConn.createIdx(idxnm, row["kb_id"], vector_size)
return settings.docStoreConn.createIdx(idxnm, row.get("kb_id",""), vector_size)
def embedding(docs, mdl, parser_config=None, callback=None):

View File

@ -1,7 +1,7 @@
import i18n from '@/locales/config';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { App, ConfigProvider, ConfigProviderProps } from 'antd';
import { App, ConfigProvider, ConfigProviderProps, theme } from 'antd';
import enUS from 'antd/locale/en_US';
import vi_VN from 'antd/locale/vi_VN';
import zhCN from 'antd/locale/zh_CN';
@ -14,7 +14,7 @@ import weekOfYear from 'dayjs/plugin/weekOfYear';
import weekYear from 'dayjs/plugin/weekYear';
import weekday from 'dayjs/plugin/weekday';
import React, { ReactNode, useEffect, useState } from 'react';
import { ThemeProvider } from './components/theme-provider';
import { ThemeProvider, useTheme } from './components/theme-provider';
import storage from './utils/authorization-util';
dayjs.extend(customParseFormat);
@ -35,7 +35,8 @@ const queryClient = new QueryClient();
type Locale = ConfigProviderProps['locale'];
const RootProvider = ({ children }: React.PropsWithChildren) => {
function Root({ children }: React.PropsWithChildren) {
const { theme: themeragflow } = useTheme();
const getLocale = (lng: string) =>
AntLanguageMap[lng as keyof typeof AntLanguageMap] ?? enUS;
@ -46,6 +47,28 @@ const RootProvider = ({ children }: React.PropsWithChildren) => {
setLocal(getLocale(lng));
});
return (
<>
<ConfigProvider
theme={{
token: {
fontFamily: 'Inter',
},
algorithm:
themeragflow === 'dark'
? theme.darkAlgorithm
: theme.defaultAlgorithm,
}}
locale={locale}
>
<App> {children}</App>
</ConfigProvider>
<ReactQueryDevtools buttonPosition={'top-left'} />
</>
);
}
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();
@ -57,22 +80,11 @@ const RootProvider = ({ children }: React.PropsWithChildren) => {
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider defaultTheme="light" storageKey="ragflow-ui-theme">
<ConfigProvider
theme={{
token: {
fontFamily: 'Inter',
},
}}
locale={locale}
>
<App> {children}</App>
</ConfigProvider>
<ReactQueryDevtools buttonPosition={'top-left'} />
<Root>{children}</Root>
</ThemeProvider>
</QueryClientProvider>
);
};
export function rootContainer(container: ReactNode) {
return <RootProvider>{container}</RootProvider>;
}

View File

@ -0,0 +1,240 @@
import Icon from '@ant-design/icons';
import { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon';
type IconComponentProps = CustomIconComponentProps;
const currentColor = 'currentColor';
const ApiSvg = () => (
<svg
viewBox="0 0 1025 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
>
<path
d="M1019.520374 56.394706L966.393742 3.257862c-2.001695-2.001695-4.503815-2.87999-7.1387-2.879991s-5.137004 1.000848-7.148912 2.879991L856.749851 98.61414C814.520204 69.916363 765.396963 55.638964 716.283935 55.638964c-64.156382 0-128.312765 24.428855-177.30324 73.429543L411.280694 256.758295c-3.880838 3.880838-3.880838 10.274008 0 14.154847L751.74254 611.364776c2.001695 2.001695 4.514028 2.890203 7.1387 2.890203 2.512332 0 5.137004-1.000848 7.1387-2.890203l127.689788-127.689788c86.338436-86.460989 96.489892-220.166077 30.454367-317.646604l95.356279-95.366491c3.880838-3.993178 3.880838-10.386348 0-14.267187zM833.321844 423.409656l-74.430391 74.440604-234.075818-234.075818 74.430391-74.430391c31.199896-31.199896 72.806566-48.490052 117.037909-48.490051 44.231342 0 85.705247 17.167603 117.037909 48.490051 31.199896 31.199896 48.490052 72.806566 48.490051 117.037909 0 44.231342-17.290155 85.705247-48.490051 117.027696zM594.987317 554.980283c-3.880838-3.880838-10.274008-3.880838-14.154847 0l-83.458446 83.458446-113.15707-113.146858 83.580998-83.580999c3.891051-3.891051 3.891051-10.284221 0-14.165059l-45.610061-45.610061c-3.880838-3.891051-10.274008-3.891051-14.154846 0l-83.580999 83.580998-53.882374-53.882374c-2.001695-2.001695-4.514028-2.87999-7.1387-2.87999-2.512332 0-5.137004 1.000848-7.138699 2.87999L128.725037 539.313952C42.386601 625.774941 32.235145 759.480028 98.27067 856.970768L2.914392 952.33726c-3.880838 3.880838-3.880838 10.274008 0 14.154846l53.136844 53.126632c2.001695 2.001695 4.514028 2.87999 7.1387 2.87999 2.634885 0 5.137004-1.000848 7.1387-2.87999l95.366491-95.356279c42.219434 28.697777 91.352888 42.985389 140.465916 42.985389 64.156382 0 128.312765-24.439068 177.30324-73.429543l127.689789-127.689788c3.880838-3.891051 3.880838-10.274008 0-14.154847l-53.882374-53.882374 83.580998-83.580998c3.880838-3.880838 3.880838-10.274008 0-14.154847l-45.865379-45.375168zM423.066186 833.665314c-31.199896 31.199896-72.806566 48.490052-117.037908 48.490051-44.231342 0-85.705247-17.167603-117.037909-48.490051-31.199896-31.199896-48.490052-72.806566-48.490052-117.037909 0-44.231342 17.167603-85.71546 48.490052-117.037909l74.430391-74.430391 234.075817 234.075818-74.430391 74.430391z m0 0"
fill={currentColor}
></path>
</svg>
);
const TeamSvg = () => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 21V19C22 17.1362 20.7252 15.5701 19 15.126M15.5 3.29076C16.9659 3.88415 18 5.32131 18 7C18 8.67869 16.9659 10.1159 15.5 10.7092M17 21C17 19.1362 17 18.2044 16.6955 17.4693C16.2895 16.4892 15.5108 15.7105 14.5307 15.3045C13.7956 15 12.8638 15 11 15H8C6.13623 15 5.20435 15 4.46927 15.3045C3.48915 15.7105 2.71046 16.4892 2.30448 17.4693C2 18.2044 2 19.1362 2 21M13.5 7C13.5 9.20914 11.7091 11 9.5 11C7.29086 11 5.5 9.20914 5.5 7C5.5 4.79086 7.29086 3 9.5 3C11.7091 3 13.5 4.79086 13.5 7Z"
stroke={currentColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const ProfileSvg = () => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20 21C20 19.6044 20 18.9067 19.8278 18.3389C19.44 17.0605 18.4395 16.06 17.1611 15.6722C16.5933 15.5 15.8956 15.5 14.5 15.5H9.5C8.10444 15.5 7.40665 15.5 6.83886 15.6722C5.56045 16.06 4.56004 17.0605 4.17224 18.3389C4 18.9067 4 19.6044 4 21M16.5 7.5C16.5 9.98528 14.4853 12 12 12C9.51472 12 7.5 9.98528 7.5 7.5C7.5 5.01472 9.51472 3 12 3C14.4853 3 16.5 5.01472 16.5 7.5Z"
stroke={currentColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const PasswordSvg = () => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17 10V8C17 5.23858 14.7614 3 12 3C9.23858 3 7 5.23858 7 8V10M12 14.5V16.5M8.8 21H15.2C16.8802 21 17.7202 21 18.362 20.673C18.9265 20.3854 19.3854 19.9265 19.673 19.362C20 18.7202 20 17.8802 20 16.2V14.8C20 13.1198 20 12.2798 19.673 11.638C19.3854 11.0735 18.9265 10.6146 18.362 10.327C17.7202 10 16.8802 10 15.2 10H8.8C7.11984 10 6.27976 10 5.63803 10.327C5.07354 10.6146 4.6146 11.0735 4.32698 11.638C4 12.2798 4 13.1198 4 14.8V16.2C4 17.8802 4 18.7202 4.32698 19.362C4.6146 19.9265 5.07354 20.3854 5.63803 20.673C6.27976 21 7.11984 21 8.8 21Z"
stroke={currentColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const LogOutSvg = () => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16 17L21 12M21 12L16 7M21 12H9M9 3H7.8C6.11984 3 5.27976 3 4.63803 3.32698C4.07354 3.6146 3.6146 4.07354 3.32698 4.63803C3 5.27976 3 6.11984 3 7.8V16.2C3 17.8802 3 18.7202 3.32698 19.362C3.6146 19.9265 4.07354 20.3854 4.63803 20.673C5.27976 21 6.11984 21 7.8 21H9"
stroke={currentColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const ModelProviderSvg = () => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.75 20.7496L11.223 21.5679C11.5066 21.7255 11.6484 21.8043 11.7986 21.8351C11.9315 21.8625 12.0685 21.8625 12.2015 21.8351C12.3516 21.8043 12.4934 21.7255 12.777 21.5679L14.25 20.7496M5.25 18.2496L3.82297 17.4568C3.52346 17.2904 3.37368 17.2072 3.26463 17.0889C3.16816 16.9842 3.09515 16.8601 3.05048 16.7249C3 16.5721 3 16.4008 3 16.0582V14.4996M3 9.4996V7.94104C3 7.5984 3 7.42708 3.05048 7.27428C3.09515 7.1391 3.16816 7.01502 3.26463 6.91033C3.37368 6.792 3.52345 6.70879 3.82297 6.54239L5.25 5.7496M9.75 3.2496L11.223 2.43128C11.5066 2.27372 11.6484 2.19494 11.7986 2.16406C11.9315 2.13672 12.0685 2.13672 12.2015 2.16406C12.3516 2.19494 12.4934 2.27372 12.777 2.43128L14.25 3.2496M18.75 5.7496L20.177 6.54239C20.4766 6.70879 20.6263 6.79199 20.7354 6.91033C20.8318 7.01502 20.9049 7.1391 20.9495 7.27428C21 7.42708 21 7.5984 21 7.94104V9.4996M21 14.4996V16.0582C21 16.4008 21 16.5721 20.9495 16.7249C20.9049 16.8601 20.8318 16.9842 20.7354 17.0889C20.6263 17.2072 20.4766 17.2904 20.177 17.4568L18.75 18.2496M9.75 10.7496L12 11.9996M12 11.9996L14.25 10.7496M12 11.9996V14.4996M3 6.9996L5.25 8.2496M18.75 8.2496L21 6.9996M12 19.4996V21.9996"
stroke={currentColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
const PromptSvg = () => (
<svg
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
>
<path
d="M509.952 260.864c-152.832 0-277.248 124.416-277.248 277.248 0 152.832 124.416 277.248 277.248 277.248 152.832 0 277.248-124.416 277.248-277.248C787.2 385.28 662.784 260.864 509.952 260.864zM509.952 746.24c-114.688 0-208.128-93.44-208.128-208.128 0-114.688 93.44-208.128 208.128-208.128s208.128 93.44 208.128 208.128C717.824 652.8 624.64 746.24 509.952 746.24z"
fill={currentColor}
></path>
<path
d="M509.952 211.2c22.016 0 39.68-17.664 39.68-39.68L549.632 77.056c0-22.016-17.664-39.68-39.68-39.68s-39.68 17.664-39.68 39.68l0 94.464C470.272 193.28 487.936 211.2 509.952 211.2z"
fill={currentColor}
></path>
<path
d="M134.144 538.88l-94.464 0c-22.016 0-39.68 17.664-39.68 39.68s17.664 39.68 39.68 39.68l94.464 0c22.016 0 39.68-17.664 39.68-39.68S155.904 538.88 134.144 538.88z"
fill={currentColor}
></path>
<path
d="M984.32 538.88l-94.464 0c-22.016 0-39.68 17.664-39.68 39.68s17.664 39.68 39.68 39.68l94.464 0c22.016 0 39.68-17.664 39.68-39.68S1006.336 538.88 984.32 538.88z"
fill={currentColor}
></path>
<path
d="M901.632 196.864c-15.616-15.616-40.704-15.616-56.064 0l-66.816 66.816c-15.616 15.616-15.616 40.704 0 56.064 7.68 7.68 17.92 11.52 28.16 11.52 10.24 0 20.224-3.84 28.16-11.52l66.816-66.816C916.992 237.568 916.992 212.224 901.632 196.864z"
fill={currentColor}
></path>
<path
d="M241.152 263.424l-66.816-66.816c-15.616-15.616-40.704-15.616-56.064 0-15.616 15.616-15.616 40.704 0 56.064l66.816 66.816c7.68 7.68 17.92 11.52 28.16 11.52 10.24 0 20.224-3.84 28.16-11.52C256.768 304.128 256.768 279.04 241.152 263.424z"
fill={currentColor}
></path>
<path
d="M605.184 866.56 414.72 866.56c-13.056 0-23.808 9.472-23.808 21.248s10.752 21.248 23.808 21.248l190.464 0c13.056 0 23.808-9.472 23.808-21.248S618.24 866.56 605.184 866.56z"
fill={currentColor}
></path>
<path
d="M577.536 944.384l-135.168 0c-13.056 0-23.808 9.472-23.808 21.248s10.752 21.248 23.808 21.248l135.168 0c13.056 0 23.808-9.472 23.808-21.248S590.592 944.384 577.536 944.384z"
fill={currentColor}
></path>
</svg>
);
const WikipediaSvg = () => (
<svg
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="200"
height="200"
>
<path
d="M515.84 559.744c-39.936 82.432-94.592 194.048-121.728 244.394667-26.282667 45.824-48.085333 39.722667-65.365333 1.237333-59.989333-141.696-183.168-390.144-241.109334-529.450667-10.709333-25.642667-18.816-42.112-26.410666-48.597333-7.722667-6.4-23.637333-10.24-47.872-11.562667C4.394667 214.741333 0 212.565333 0 208.981333v-19.413333l2.218667-1.92c39.424-0.213333 230.442667 0 230.442666 0l2.176 1.92v18.517333c0 5.077333-3.2 7.509333-9.6 7.509334l-24.064 1.322666c-20.693333 1.237333-31.018667 6.997333-31.018666 18.602667 0 5.76 2.261333 14.08 7.082666 25.642667 46.165333 112.896 205.568 448.896 205.568 448.896l5.802667 1.962666 102.869333-205.226666-20.565333-45.525334-70.741333-139.264s-13.568-27.904-18.261334-37.205333c-31.061333-61.568-30.378667-64.768-61.738666-68.992-8.832-0.981333-13.354667-2.133333-13.354667-6.357333v-19.968l2.56-1.92h183.125333l4.821334 1.578666v19.242667c0 4.48-3.242667 6.4-9.685334 6.4l-13.141333 2.005333c-33.792 2.602667-28.202667 16.256-5.802667 60.672l67.498667 138.752 75.008-149.504c12.501333-27.306667 9.941333-34.176 4.736-40.405333-2.986667-3.584-13.013333-9.386667-34.645333-10.24l-8.576-0.896a9.728 9.728 0 0 1-6.186667-2.176 6.4 6.4 0 0 1-2.858667-5.504v-18.218667l2.602667-1.92c53.205333-0.341333 172.501333 0 172.501333 0l2.517334 1.92v18.602667c0 5.162667-2.517333 7.594667-8.234667 7.594667-27.562667 1.28-33.365333 4.053333-43.648 18.730666-5.12 7.936-16 25.130667-27.562667 44.330667l-98.176 182.314667-2.773333 5.76 119.125333 243.712 7.253334 2.048 187.562666-445.354667c6.570667-18.005333 5.504-30.805333-2.730666-38.186667-8.405333-7.338667-14.762667-11.648-36.565334-12.586666l-17.92-0.682667a10.88 10.88 0 0 1-6.485333-1.92c-1.834667-1.237333-3.072-3.2-3.072-5.077333v-18.602667l2.517333-1.92h211.669334l1.749333 1.92v18.645333c0 5.077333-3.157333 7.68-8.917333 7.68-27.648 1.28-48.085333 7.68-61.568 17.962667-13.397333 10.88-23.765333 26.282667-31.402667 45.525333 0 0-172.501333 395.008-231.509333 526.464-22.4 42.965333-44.928 39.125333-64.128-1.322666-24.362667-49.962667-75.648-161.536-112.896-243.626667l2.261333-1.536z"
fill={currentColor}
></path>
</svg>
);
const KeywordSvg = () => (
<svg
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="200"
height="200"
>
<path
d="M419.999992 631.999988v-136.699998c74.443999-50.009999 119.999998-134.033997 119.999997-223.299995C539.999989 123.113998 418.885992 0 269.999995 0S0 123.113998 0 271.999995c0 89.267998 45.555999 173.291997 119.999998 223.299995V871.999983c0 7.968 3.164 15.586 8.789999 21.21l119.999998 121.999997A29.903999 29.903999 0 0 0 269.999995 1023.99998a29.899999 29.899999 0 0 0 21.209999-8.79l119.999998-121.999997c11.718-11.718 11.718-30.703999 0-42.422L372.421993 811.999984l38.789999-38.789999c11.718-11.718 11.718-30.703999 0-42.421999L372.421993 691.999986l38.789999-38.789999A29.979999 29.979999 0 0 0 419.999992 631.999988z m-149.999997-329.999994c-49.627999 0-89.999998-40.371999-89.999999-89.999998s40.371999-89.999998 89.999999-89.999998 89.999998 40.371999 89.999998 89.999998-40.371999 89.999998-89.999998 89.999998z"
fill={currentColor}
></path>
<path
d="M933.999982 361.999993c-39.059999 0-72.047999 25.156-84.473999 59.999999H753.999985a30.011999 30.011999 0 0 0-26.835999 16.581999L675.455987 541.999989H509.99999c-16.582 0-29.999999 13.418-29.999999 30s13.418 29.999999 29.999999 29.999999h183.999996a30.011999 30.011999 0 0 0 26.836-16.581999L772.543985 481.999991h76.981998c12.426 34.843999 45.413999 59.999999 84.473999 59.999998 49.627999 0 89.999998-40.371999 89.999998-89.999998s-40.371999-89.999998-89.999998-89.999998zM933.999982 601.999988c-39.059999 0-72.047999 25.156-84.473999 59.999999H509.99999c-16.582 0-29.999999 13.418-29.999999 29.999999s13.418 29.999999 29.999999 30h339.525993c12.426 34.843999 45.413999 59.999999 84.473999 59.999999 49.627999 0 89.999998-40.371999 89.999998-89.999999s-40.371999-89.999998-89.999998-89.999998zM933.999982 841.999984c-39.059999 0-72.047999 25.156-84.473999 59.999998h-76.981998l-51.707999-103.417998A30.011999 30.011999 0 0 0 693.999986 781.999985h-183.999996c-16.582 0-29.999999 13.418-29.999999 29.999999s13.418 29.999999 29.999999 30h165.455997l51.707999 103.417998C732.261986 955.583981 742.631985 963.999981 753.999985 963.999981h95.525998c12.426 34.843999 45.413999 59.999999 84.473999 59.999999 49.627999 0 89.999998-42.371999 89.999998-91.999998s-40.371999-89.999998-89.999998-89.999998zM933.999982 121.999998c-39.059999 0-72.047999 25.156-84.473999 59.999998H753.999985c-10.02 0-19.394 5.01-24.959999 13.36L617.945988 361.999993H569.999989c-16.582 0-29.999999 13.418-30 29.999999s13.418 29.999999 30 30h63.999999c10.02 0 19.394-5.01 24.959999-13.36L770.053985 241.999995h79.471998c12.426 34.843999 45.413999 59.999999 84.473999 59.999999 49.627999 0 89.999998-40.371999 89.999998-89.999998s-40.371999-89.999998-89.999998-89.999998z"
fill={currentColor}
></path>
</svg>
);
const GitHubSvg = () => (
<svg
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="200"
height="200"
>
<path
fill={currentColor}
d="M938.666667 512a426.666667 426.666667 0 0 1-291.84 404.48 22.186667 22.186667 0 0 1-19.2-2.986667 21.76 21.76 0 0 1-8.96-17.493333v-113.92a170.666667 170.666667 0 0 0-21.333334-87.893333 10.666667 10.666667 0 0 1 0-11.52 11.52 11.52 0 0 1 8.533334-5.973334c104.106667-10.666667 162.133333-52.053333 162.133333-164.693333a200.96 200.96 0 0 0-50.773333-143.36 183.466667 183.466667 0 0 0 8.106666-51.2 184.746667 184.746667 0 0 0-6.4-46.08 20.906667 20.906667 0 0 0-22.613333-15.36 189.866667 189.866667 0 0 0-104.106667 50.346667 422.826667 422.826667 0 0 0-160.426666 0A189.866667 189.866667 0 0 0 327.68 256a20.906667 20.906667 0 0 0-22.613333 15.36A184.746667 184.746667 0 0 0 298.666667 317.44a183.466667 183.466667 0 0 0 8.106666 51.2A200.96 200.96 0 0 0 256 512c0 118.613333 64.426667 158.293333 182.613333 168.106667a158.293333 158.293333 0 0 0-29.44 65.28v5.12a29.013333 29.013333 0 0 0 0 5.973333 25.173333 25.173333 0 0 1-27.306666 21.76 42.666667 42.666667 0 0 1-18.346667-5.12 227.84 227.84 0 0 1-60.586667-53.76 430.506667 430.506667 0 0 0-34.133333-34.56 116.906667 116.906667 0 0 0-25.173333-16.64 20.906667 20.906667 0 0 0-20.48 0 21.333333 21.333333 0 0 0-9.813334 17.92v2.56a21.333333 21.333333 0 0 0 9.813334 17.92 193.706667 193.706667 0 0 1 39.253333 44.8 282.026667 282.026667 0 0 0 67.84 73.386667 105.813333 105.813333 0 0 0 59.733333 17.92h15.36V896a21.76 21.76 0 0 1-8.96 17.493333 22.186667 22.186667 0 0 1-19.2 2.986667A426.666667 426.666667 0 1 1 938.666667 512z"
></path>
</svg>
);
const QWeatherSvg = () => (
<svg
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="200"
height="200"
>
<path
fill={currentColor}
d="M953.6 736c-57.6 51.2-89.6 89.6-166.4 102.4 166.4-160 185.6-422.4 44.8-608-64-89.6-166.4-147.2-275.2-172.8-108.8-19.2-224 0-326.4 57.6-96 57.6-172.8 147.2-204.8 256-32 108.8-32 224 19.2 326.4 44.8 102.4 128 185.6 230.4 236.8 102.4 44.8 217.6 57.6 326.4 25.6 96 6.4 192-12.8 275.2-57.6 70.4-38.4 102.4-96 147.2-160l-70.4-6.4z m-537.6 172.8c-83.2-12.8-166.4-51.2-224-108.8-121.6-128-140.8-326.4-32-467.2 51.2-70.4 128-121.6 211.2-140.8 76.8-19.2 166.4-12.8 243.2 25.6 76.8 38.4 140.8 102.4 172.8 185.6 32 76.8 38.4 166.4 12.8 249.6-25.6 83.2-83.2 153.6-153.6 198.4-25.6 0-44.8-6.4-70.4-6.4-83.2-19.2-153.6-70.4-204.8-134.4-25.6-32-44.8-76.8-44.8-121.6 0-38.4 12.8-70.4 32-96 32-38.4 76.8-57.6 121.6-57.6 12.8 0 25.6 0 38.4 6.4 6.4 0 12.8 0 12.8 6.4 25.6 0 44.8 19.2 57.6 38.4 25.6 44.8 12.8 96-25.6 128-32 12.8-64 19.2-89.6 6.4h-12.8c0 6.4-6.4 6.4 0 12.8 19.2 32 51.2 51.2 89.6 51.2 25.6 0 51.2-6.4 76.8-19.2 57.6-32 96-89.6 96-153.6 0-38.4-12.8-70.4-32-102.4 0 0 0-6.4-6.4-6.4 0 0 0-6.4-6.4-6.4-51.2-64-128-102.4-217.6-102.4-83.2 0-153.6 32-211.2 89.6-83.2 83.2-102.4 211.2-44.8 320 51.2 89.6 128 160 217.6 204.8h-6.4z"
></path>
</svg>
);
export const ApiIcon = (props: Partial<IconComponentProps>) => (
<Icon component={ApiSvg} {...props} />
);
export const TeamIcon = (props: Partial<IconComponentProps>) => (
<Icon component={TeamSvg} {...props} />
);
export const ProfileIcon = (props: Partial<IconComponentProps>) => (
<Icon component={ProfileSvg} {...props} />
);
export const PasswordIcon = (props: Partial<IconComponentProps>) => (
<Icon component={PasswordSvg} {...props} />
);
export const LogOutIcon = (props: Partial<IconComponentProps>) => (
<Icon component={LogOutSvg} {...props} />
);
export const ModelProviderIcon = (props: Partial<IconComponentProps>) => (
<Icon component={ModelProviderSvg} {...props} />
);
export const PromptIcon = (props: Partial<IconComponentProps>) => (
<Icon component={PromptSvg} {...props} />
);
export const WikipediaIcon = (props: Partial<IconComponentProps>) => (
<Icon component={WikipediaSvg} {...props} />
);
export const KeywordIcon = (props: Partial<IconComponentProps>) => (
<Icon component={KeywordSvg} {...props} />
);
export const GitHubIcon = (props: Partial<IconComponentProps>) => (
<Icon component={GitHubSvg} {...props} />
);
export const QWeatherIcon = (props: Partial<IconComponentProps>) => (
<Icon component={QWeatherSvg} {...props} />
);

View File

@ -2,5 +2,5 @@
width="24" height="24">
<path
d="M1019.520374 56.394706L966.393742 3.257862c-2.001695-2.001695-4.503815-2.87999-7.1387-2.879991s-5.137004 1.000848-7.148912 2.879991L856.749851 98.61414C814.520204 69.916363 765.396963 55.638964 716.283935 55.638964c-64.156382 0-128.312765 24.428855-177.30324 73.429543L411.280694 256.758295c-3.880838 3.880838-3.880838 10.274008 0 14.154847L751.74254 611.364776c2.001695 2.001695 4.514028 2.890203 7.1387 2.890203 2.512332 0 5.137004-1.000848 7.1387-2.890203l127.689788-127.689788c86.338436-86.460989 96.489892-220.166077 30.454367-317.646604l95.356279-95.366491c3.880838-3.993178 3.880838-10.386348 0-14.267187zM833.321844 423.409656l-74.430391 74.440604-234.075818-234.075818 74.430391-74.430391c31.199896-31.199896 72.806566-48.490052 117.037909-48.490051 44.231342 0 85.705247 17.167603 117.037909 48.490051 31.199896 31.199896 48.490052 72.806566 48.490051 117.037909 0 44.231342-17.290155 85.705247-48.490051 117.027696zM594.987317 554.980283c-3.880838-3.880838-10.274008-3.880838-14.154847 0l-83.458446 83.458446-113.15707-113.146858 83.580998-83.580999c3.891051-3.891051 3.891051-10.284221 0-14.165059l-45.610061-45.610061c-3.880838-3.891051-10.274008-3.891051-14.154846 0l-83.580999 83.580998-53.882374-53.882374c-2.001695-2.001695-4.514028-2.87999-7.1387-2.87999-2.512332 0-5.137004 1.000848-7.138699 2.87999L128.725037 539.313952C42.386601 625.774941 32.235145 759.480028 98.27067 856.970768L2.914392 952.33726c-3.880838 3.880838-3.880838 10.274008 0 14.154846l53.136844 53.126632c2.001695 2.001695 4.514028 2.87999 7.1387 2.87999 2.634885 0 5.137004-1.000848 7.1387-2.87999l95.366491-95.356279c42.219434 28.697777 91.352888 42.985389 140.465916 42.985389 64.156382 0 128.312765-24.439068 177.30324-73.429543l127.689789-127.689788c3.880838-3.891051 3.880838-10.274008 0-14.154847l-53.882374-53.882374 83.580998-83.580998c3.880838-3.880838 3.880838-10.274008 0-14.154847l-45.865379-45.375168zM423.066186 833.665314c-31.199896 31.199896-72.806566 48.490052-117.037908 48.490051-44.231342 0-85.705247-17.167603-117.037909-48.490051-31.199896-31.199896-48.490052-72.806566-48.490052-117.037909 0-44.231342 17.167603-85.71546 48.490052-117.037909l74.430391-74.430391 234.075817 234.075818-74.430391 74.430391z m0 0"
fill="#2C2C2C" p-id="4307"></path>
fill="#667085" p-id="4307"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,5 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M13.3333 5.00002V4.33335C13.3333 3.39993 13.3333 2.93322 13.1517 2.5767C12.9919 2.2631 12.7369 2.00813 12.4233 1.84834C12.0668 1.66669 11.6001 1.66669 10.6667 1.66669H9.33333C8.39991 1.66669 7.9332 1.66669 7.57668 1.84834C7.26308 2.00813 7.00811 2.2631 6.84832 2.5767C6.66667 2.93322 6.66667 3.39993 6.66667 4.33335V5.00002M8.33333 9.58335V13.75M11.6667 9.58335V13.75M2.5 5.00002H17.5M15.8333 5.00002V14.3334C15.8333 15.7335 15.8333 16.4336 15.5608 16.9683C15.3212 17.4387 14.9387 17.8212 14.4683 18.0609C13.9335 18.3334 13.2335 18.3334 11.8333 18.3334H8.16667C6.76654 18.3334 6.06647 18.3334 5.53169 18.0609C5.06129 17.8212 4.67883 17.4387 4.43915 16.9683C4.16667 16.4336 4.16667 15.7335 4.16667 14.3334V5.00002"
stroke="#475467" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
stroke="#667085" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
</svg>

Before

Width:  |  Height:  |  Size: 935 B

After

Width:  |  Height:  |  Size: 935 B

View File

@ -2,26 +2,26 @@
width="200" height="200">
<path
d="M509.952 260.864c-152.832 0-277.248 124.416-277.248 277.248 0 152.832 124.416 277.248 277.248 277.248 152.832 0 277.248-124.416 277.248-277.248C787.2 385.28 662.784 260.864 509.952 260.864zM509.952 746.24c-114.688 0-208.128-93.44-208.128-208.128 0-114.688 93.44-208.128 208.128-208.128s208.128 93.44 208.128 208.128C717.824 652.8 624.64 746.24 509.952 746.24z"
fill="#2c2c2c" p-id="4297"></path>
fill="#667085" p-id="4297"></path>
<path
d="M509.952 211.2c22.016 0 39.68-17.664 39.68-39.68L549.632 77.056c0-22.016-17.664-39.68-39.68-39.68s-39.68 17.664-39.68 39.68l0 94.464C470.272 193.28 487.936 211.2 509.952 211.2z"
fill="#2c2c2c" p-id="4298"></path>
fill="#667085" p-id="4298"></path>
<path
d="M134.144 538.88l-94.464 0c-22.016 0-39.68 17.664-39.68 39.68s17.664 39.68 39.68 39.68l94.464 0c22.016 0 39.68-17.664 39.68-39.68S155.904 538.88 134.144 538.88z"
fill="#2c2c2c" p-id="4299"></path>
fill="#667085" p-id="4299"></path>
<path
d="M984.32 538.88l-94.464 0c-22.016 0-39.68 17.664-39.68 39.68s17.664 39.68 39.68 39.68l94.464 0c22.016 0 39.68-17.664 39.68-39.68S1006.336 538.88 984.32 538.88z"
fill="#2c2c2c" p-id="4300"></path>
fill="#667085" p-id="4300"></path>
<path
d="M901.632 196.864c-15.616-15.616-40.704-15.616-56.064 0l-66.816 66.816c-15.616 15.616-15.616 40.704 0 56.064 7.68 7.68 17.92 11.52 28.16 11.52 10.24 0 20.224-3.84 28.16-11.52l66.816-66.816C916.992 237.568 916.992 212.224 901.632 196.864z"
fill="#2c2c2c" p-id="4301"></path>
fill="#667085" p-id="4301"></path>
<path
d="M241.152 263.424l-66.816-66.816c-15.616-15.616-40.704-15.616-56.064 0-15.616 15.616-15.616 40.704 0 56.064l66.816 66.816c7.68 7.68 17.92 11.52 28.16 11.52 10.24 0 20.224-3.84 28.16-11.52C256.768 304.128 256.768 279.04 241.152 263.424z"
fill="#2c2c2c" p-id="4302"></path>
fill="#667085" p-id="4302"></path>
<path
d="M605.184 866.56 414.72 866.56c-13.056 0-23.808 9.472-23.808 21.248s10.752 21.248 23.808 21.248l190.464 0c13.056 0 23.808-9.472 23.808-21.248S618.24 866.56 605.184 866.56z"
fill="#2c2c2c" p-id="4303"></path>
fill="#667085" p-id="4303"></path>
<path
d="M577.536 944.384l-135.168 0c-13.056 0-23.808 9.472-23.808 21.248s10.752 21.248 23.808 21.248l135.168 0c13.056 0 23.808-9.472 23.808-21.248S590.592 944.384 577.536 944.384z"
fill="#2c2c2c" p-id="4304"></path>
fill="#667085" p-id="4304"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -16,4 +16,5 @@
.apiLinkText {
.linkText();
margin: 0 !important;
background-color: rgba(255, 255, 255, 0.1);
}

View File

@ -4,5 +4,5 @@
.codeText {
padding: 10px;
background-color: #e8e8ea;
background-color: #ffffff09;
}

View File

@ -90,7 +90,7 @@ export const EditableCell: React.FC<EditableCellProps> = ({
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div onClick={toggleEdit} className="truncate">
<div onClick={toggleEdit} className="editable-cell-value-wrap">
<Text>{children}</Text>
</div>
);

View File

@ -15,5 +15,5 @@
white-space: break-spaces;
background-color: rgba(129, 139, 152, 0.12);
border-radius: 4px;
color: rgb(31, 35, 40);
color: var(--ant-color-text-base);
}

View File

@ -312,81 +312,97 @@ function fallbackRender({ error }: FallbackProps) {
const IndentedTree = ({ data, show, style = {} }: IProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const graphRef = useRef<Graph | null>(null);
const render = useCallback(async (data: TreeData) => {
const graph: Graph = new Graph({
container: containerRef.current!,
x: 60,
node: {
type: 'indented',
style: {
size: (d) => [d.id.length * 6 + 10, 20],
labelBackground: (datum) => datum.id === rootId,
labelBackgroundRadius: 0,
labelBackgroundFill: '#576286',
labelFill: (datum) => (datum.id === rootId ? '#fff' : '#666'),
labelText: (d) => d.style?.labelText || d.id,
labelTextAlign: (datum) => (datum.id === rootId ? 'center' : 'left'),
labelTextBaseline: 'top',
color: (datum: any) => {
const depth = graph.getAncestorsData(datum.id, 'tree').length - 1;
return COLORS[depth % COLORS.length] || '#576286';
},
},
state: {
selected: {
lineWidth: 0,
labelFill: '#40A8FF',
labelBackground: true,
labelFontWeight: 'normal',
labelBackgroundFill: '#e8f7ff',
labelBackgroundRadius: 10,
},
},
},
edge: {
type: 'indented',
style: {
radius: 16,
lineWidth: 2,
sourcePort: 'out',
targetPort: 'in',
stroke: (datum: any) => {
const depth = graph.getAncestorsData(datum.source, 'tree').length;
return COLORS[depth % COLORS.length] || 'black';
},
},
},
layout: {
type: 'indented',
direction: 'LR',
isHorizontal: true,
indent: 40,
getHeight: () => 20,
getVGap: () => 10,
},
behaviors: [
'scroll-canvas',
'collapse-expand-tree',
{
type: 'click-select',
enable: (event: any) =>
event.targetType === 'node' && event.target.id !== rootId,
},
],
});
if (graphRef.current) {
graphRef.current.destroy();
const assignIds = React.useCallback(function assignIds(
node: TreeData,
parentId: string = '',
index = 0,
) {
if (!node.id) node.id = parentId ? `${parentId}-${index}` : 'root';
if (node.children) {
node.children.forEach((child, idx) => assignIds(child, node.id, idx));
}
graphRef.current = graph;
graph?.setData(treeToGraphData(data));
graph?.render();
}, []);
const render = useCallback(
async (data: TreeData) => {
const graph: Graph = new Graph({
container: containerRef.current!,
x: 60,
node: {
type: 'indented',
style: {
size: (d) => [d.id.length * 6 + 10, 20],
labelBackground: (datum) => datum.id === rootId,
labelBackgroundRadius: 0,
labelBackgroundFill: '#576286',
labelFill: (datum) => (datum.id === rootId ? '#fff' : '#666'),
labelText: (d) => d.style?.labelText || d.id,
labelTextAlign: (datum) =>
datum.id === rootId ? 'center' : 'left',
labelTextBaseline: 'top',
color: (datum: any) => {
const depth = graph.getAncestorsData(datum.id, 'tree').length - 1;
return COLORS[depth % COLORS.length] || '#576286';
},
},
state: {
selected: {
lineWidth: 0,
labelFill: '#40A8FF',
labelBackground: true,
labelFontWeight: 'normal',
labelBackgroundFill: '#e8f7ff',
labelBackgroundRadius: 10,
},
},
},
edge: {
type: 'indented',
style: {
radius: 16,
lineWidth: 2,
sourcePort: 'out',
targetPort: 'in',
stroke: (datum: any) => {
const depth = graph.getAncestorsData(datum.source, 'tree').length;
return COLORS[depth % COLORS.length] || 'black';
},
},
},
layout: {
type: 'indented',
direction: 'LR',
isHorizontal: true,
indent: 40,
getHeight: () => 20,
getVGap: () => 10,
},
behaviors: [
'scroll-canvas',
'collapse-expand-tree',
{
type: 'click-select',
enable: (event: any) =>
event.targetType === 'node' && event.target.id !== rootId,
},
],
});
if (graphRef.current) {
graphRef.current.destroy();
}
graphRef.current = graph;
assignIds(data);
graph?.setData(treeToGraphData(data));
graph?.render();
},
[assignIds],
);
useEffect(() => {
if (!isEmpty(data)) {
render(data);

View File

@ -269,7 +269,7 @@ const LlmSettingItems = ({ prefix, formItemLayout = {} }: IProps) => {
>
<Slider
className={styles.variableSlider}
max={8192}
max={128000}
disabled={disabled}
/>
</Form.Item>
@ -278,7 +278,7 @@ const LlmSettingItems = ({ prefix, formItemLayout = {} }: IProps) => {
<InputNumber
disabled={disabled}
className={styles.sliderInputNumber}
max={8192}
max={128000}
min={0}
/>
</Form.Item>

View File

@ -1,6 +1,6 @@
.messageInputWrapper {
margin-right: 20px;
background-color: #f5f5f8;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 8px;
:global(.ant-input-affix-wrapper) {
border-bottom-right-radius: 0;

View File

@ -27,6 +27,7 @@ import {
} from 'antd';
import classNames from 'classnames';
import get from 'lodash/get';
import { Paperclip } from 'lucide-react';
import {
ChangeEventHandler,
memo,
@ -36,7 +37,6 @@ import {
useState,
} from 'react';
import FileIcon from '../file-icon';
import SvgIcon from '../svg-icon';
import styles from './index.less';
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
@ -98,7 +98,6 @@ const MessageInput = ({
const { data: documentInfos, setDocumentIds } = useFetchDocumentInfosByIds();
const { uploadAndParseDocument } = useUploadAndParseDocument(uploadMethod);
const conversationIdRef = useRef(conversationId);
const [fileList, setFileList] = useState<UploadFile[]>([]);
const handlePreview = async (file: UploadFile) => {
@ -225,14 +224,7 @@ const MessageInput = ({
<Button
type={'text'}
disabled={disabled}
icon={
<SvgIcon
name="paper-clip"
width={18}
height={22}
disabled={disabled}
></SvgIcon>
}
icon={<Paperclip></Paperclip>}
></Button>
</Upload>
)}

View File

@ -1,3 +1,4 @@
import { PromptIcon } from '@/assets/icon/Icon';
import CopyToClipboard from '@/components/copy-to-clipboard';
import { useSetModalState } from '@/hooks/common-hooks';
import { IRemoveMessageById } from '@/hooks/logic-hooks';
@ -12,7 +13,6 @@ import {
import { Radio, Tooltip } from 'antd';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import SvgIcon from '../svg-icon';
import FeedbackModal from './feedback-modal';
import { useRemoveMessage, useSendFeedback, useSpeech } from './hooks';
import PromptModal from './prompt-modal';
@ -70,7 +70,7 @@ export const AssistantGroupButton = ({
)}
{prompt && (
<Radio.Button value="e" onClick={showPromptModal}>
<SvgIcon name={`prompt`} width={16}></SvgIcon>
<PromptIcon style={{ fontSize: '16px' }} />
</Radio.Button>
)}
</Radio.Group>

View File

@ -6,10 +6,6 @@
.messageItemSectionLeft {
width: 80%;
}
.messageItemSectionRight {
// width: 80%;
// max-width: 50vw;
}
.messageItemContent {
display: inline-flex;
gap: 20px;
@ -36,10 +32,17 @@
background-color: #e6f4ff;
word-break: break-all;
}
.messageTextDark {
.chunkText();
.messageTextBase();
background-color: #1668dc;
word-break: break-all;
}
.messageUserText {
.chunkText();
.messageTextBase();
background-color: rgb(248, 247, 247);
background-color: rgba(255, 255, 255, 0.3);
word-break: break-all;
text-align: justify;
}

View File

@ -18,6 +18,7 @@ import { Avatar, Button, Flex, List, Space, Typography } from 'antd';
import FileIcon from '../file-icon';
import IndentedTreeModal from '../indented-tree/modal';
import NewDocumentLink from '../new-document-link';
import { useTheme } from '../theme-provider';
import { AssistantGroupButton, UserGroupButton } from './group-button';
import styles from './index.less';
@ -47,6 +48,7 @@ const MessageItem = ({
regenerateMessage,
showLikeButton = true,
}: IProps) => {
const { theme } = useTheme();
const isAssistant = item.role === MessageType.Assistant;
const isUser = item.role === MessageType.User;
const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds();
@ -139,7 +141,11 @@ const MessageItem = ({
</Space>
<div
className={
isAssistant ? styles.messageText : styles.messageUserText
isAssistant
? theme === 'dark'
? styles.messageTextDark
: styles.messageText
: styles.messageUserText
}
>
<MarkdownContent
@ -181,8 +187,8 @@ const MessageItem = ({
dataSource={documentList}
renderItem={(item) => {
// TODO:
const fileThumbnail =
documentThumbnails[item.id] || documentThumbnails[item.id];
// const fileThumbnail =
// documentThumbnails[item.id] || documentThumbnails[item.id];
const fileExtension = getExtension(item.name);
return (
<List.Item>

View File

@ -35,7 +35,7 @@ const RetrievalDocuments = ({
>
<Space>
<span>
{selectedDocumentIds.length ?? 0}/{documents.length}
{selectedDocumentIds?.length ?? 0}/{documents?.length ?? 0}
</span>
{t('knowledgeDetails.filesSelected')}
</Space>

View File

@ -1,6 +1,6 @@
import { createContext, useContext, useEffect, useState } from 'react';
import React, { createContext, useContext, useEffect, useState } from 'react';
type Theme = 'dark' | 'light' | 'system';
type Theme = 'dark' | 'light';
type ThemeProviderProps = {
children: React.ReactNode;
@ -14,7 +14,7 @@ type ThemeProviderState = {
};
const initialState: ThemeProviderState = {
theme: 'system',
theme: 'light',
setTheme: () => null,
};
@ -22,7 +22,7 @@ const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
export function ThemeProvider({
children,
defaultTheme = 'system',
defaultTheme = 'light',
storageKey = 'vite-ui-theme',
...props
}: ThemeProviderProps) {
@ -32,32 +32,19 @@ export function ThemeProvider({
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove('light', 'dark');
if (theme === 'system') {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
.matches
? 'dark'
: 'light';
root.classList.add(systemTheme);
return;
}
localStorage.setItem(storageKey, theme);
root.classList.add(theme);
}, [theme]);
const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};
}, [storageKey, theme]);
return (
<ThemeProviderContext.Provider {...props} value={value}>
<ThemeProviderContext.Provider
{...props}
value={{
theme,
setTheme,
}}
>
{children}
</ThemeProviderContext.Provider>
);

View File

@ -123,6 +123,6 @@ export {
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
NavigationMenuViewport,
navigationMenuTriggerStyle,
};

View File

@ -48,6 +48,66 @@
color: white;
}
}
:global(.ant-radio-button-wrapper-checked.dark) {
border-radius: 0px !important;
& a {
color: white;
}
}
:global(.ant-radio-button-wrapper-checked.dark.first) {
border-top-left-radius: 6px !important;
border-bottom-left-radius: 6px !important;
& a {
color: white;
}
}
:global(.ant-radio-button-wrapper-checked.dark.last) {
border-top-right-radius: 6px !important;
border-bottom-right-radius: 6px !important;
& a {
color: white;
}
}
}
.radioGroupDark {
& label {
height: 40px;
line-height: 40px;
border: 0 !important;
background-color: rgba(249, 249, 249, 0.25);
font-weight: @fontWeight700;
color: rgba(29, 25, 41, 1);
&::before {
display: none !important;
}
}
:global(.ant-radio-button-wrapper-checked) {
border-radius: 6px !important;
& a {
color: white;
}
}
:global(.ant-radio-button-wrapper-checked.dark) {
border-radius: 0px !important;
& a {
color: white;
}
}
:global(.ant-radio-button-wrapper-checked.dark.first) {
border-top-left-radius: 6px !important;
border-bottom-left-radius: 6px !important;
& a {
color: white;
}
}
:global(.ant-radio-button-wrapper-checked.dark.last) {
border-top-right-radius: 6px !important;
border-bottom-right-radius: 6px !important;
& a {
color: white;
}
}
}
.ant-radio-button-wrapper-checked {
@ -55,6 +115,6 @@
}
.radioButtonIcon {
vertical-align: middle;
max-width: 16px;
max-height: 16px;
max-width: 15px;
max-height: 15px;
}

View File

@ -10,6 +10,7 @@ import { MouseEventHandler, useCallback, useMemo } from 'react';
import { useLocation } from 'umi';
import Toolbar from '../right-toolbar';
import { useTheme } from '@/components/theme-provider';
import styles from './index.less';
const { Header } = Layout;
@ -22,7 +23,7 @@ const RagHeader = () => {
const { pathname } = useLocation();
const { t } = useTranslate('header');
const appConf = useFetchAppConf();
const { theme: themeRag } = useTheme();
const tagsData = useMemo(
() => [
{ path: '/knowledge', name: t('knowledgeBase'), icon: KnowledgeBaseIcon },
@ -78,11 +79,17 @@ const RagHeader = () => {
<Radio.Group
defaultValue="a"
buttonStyle="solid"
className={styles.radioGroup}
className={
themeRag === 'dark' ? styles.radioGroupDark : styles.radioGroup
}
value={currentPath}
>
{tagsData.map((item) => (
<Radio.Button value={item.name} key={item.name}>
{tagsData.map((item, index) => (
<Radio.Button
className={`${themeRag === 'dark' ? 'dark' : 'light'} ${index === 0 ? 'first' : ''} ${index === tagsData.length - 1 ? 'last' : ''}`}
value={item.name}
key={item.name}
>
<a href={item.path}>
<Flex
align="center"

View File

@ -11,9 +11,13 @@
text-align: center;
height: 32px;
border-radius: 50%;
background-color: rgba(242, 243, 245, 1);
background-color: rgba(242, 243, 245, 0.4);
vertical-align: middle;
cursor: pointer;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.language {

View File

@ -5,9 +5,11 @@ import camelCase from 'lodash/camelCase';
import React from 'react';
import User from '../user';
import { useTheme } from '@/components/theme-provider';
import { LanguageList, LanguageMap } from '@/constants/common';
import { useChangeLanguage } from '@/hooks/logic-hooks';
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import { MoonIcon, SunIcon } from 'lucide-react';
import styled from './index.less';
const Circle = ({ children, ...restProps }: React.PropsWithChildren) => {
@ -25,6 +27,8 @@ const handleGithubCLick = () => {
const RightToolBar = () => {
const { t } = useTranslate('common');
const changeLanguage = useChangeLanguage();
const { setTheme, theme } = useTheme();
const {
data: { language = 'English' },
} = useFetchUserInfo();
@ -40,6 +44,13 @@ const RightToolBar = () => {
return [...pre!, { type: 'divider' }, cur];
}, []);
const onMoonClick = React.useCallback(() => {
setTheme('light');
}, [setTheme]);
const onSunClick = React.useCallback(() => {
setTheme('dark');
}, [setTheme]);
return (
<div className={styled.toolbarWrapper}>
<Space wrap size={16}>
@ -52,9 +63,13 @@ const RightToolBar = () => {
<Circle>
<GithubOutlined onClick={handleGithubCLick} />
</Circle>
{/* <Circle>
<MonIcon />
</Circle> */}
<Circle>
{theme === 'dark' ? (
<MoonIcon onClick={onMoonClick} size={20} />
) : (
<SunIcon onClick={onSunClick} size={20} />
)}
</Circle>
<User></User>
</Space>
</div>

View File

@ -38,7 +38,7 @@
}
tr:nth-child(even) {
background-color: #f2f2f2;
background-color: #f2f2f22a;
}
}

View File

@ -1,7 +1,7 @@
@fontWeight600: 600;
@fontWeight700: 700;
@grayBackground: rgba(247, 248, 250, 1);
@grayBackground: rgba(247, 248, 250, 0.1);
@gray2: rgba(29, 25, 41, 1);
@gray3: rgba(52, 48, 62, 1);
@gray8: rgba(165, 163, 169, 1);

View File

@ -2,6 +2,7 @@ import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import { createTranslationTable, flattenObject } from './until';
import translation_en from './en';
import translation_es from './es';
import translation_id from './id';
@ -19,7 +20,15 @@ const resources = {
es: translation_es,
vi: translation_vi,
};
const enFlattened = flattenObject(translation_en);
const viFlattened = flattenObject(translation_vi);
const esFlattened = flattenObject(translation_es);
const zhFlattened = flattenObject(translation_zh);
const zh_traditionalFlattened = flattenObject(translation_zh_traditional);
export const translationTable = createTranslationTable(
[enFlattened, viFlattened, esFlattened, zhFlattened, zh_traditionalFlattened],
['English', 'Vietnamese', 'Spanish', 'zh', 'zh-TRADITIONAL'],
);
i18n
.use(initReactI18next)
.use(LanguageDetector)

View File

@ -303,9 +303,9 @@ The above is the content you need to summarize.`,
randomSeed: 'Random seed',
randomSeedMessage: 'Random seed is required',
entityTypes: 'Entity types',
vietnamese: 'Vietamese',
pageRank: 'Page rank',
pageRankTip: `This is used to boost the relevance score. The relevance score with all the retrieved chunks will plus this number.
When you want to search the given knowledge base at first place, set a higher pagerank score than others.`,
pageRankTip: `This is used to boost the relevance score. The relevance score with all the retrieved chunks will plus this number, When you want to search the given knowledge base at first place, set a higher pagerank score than others.`,
},
chunk: {
chunk: 'Chunk',

View File

@ -277,6 +277,7 @@ export default {
multiTurn: 'Optimización de múltiples turnos',
multiTurnTip:
'En conversaciones de múltiples rondas, la consulta a la base de conocimiento se optimiza. El gran modelo se llamará para consumir tokens adicionales.',
description: 'Description of assistant',
},
setting: {
profile: 'Perfil',

View File

@ -449,6 +449,7 @@ export default {
multiTurnTip:
'Dalam percakapan multi-putaran, kueri ke basis pengetahuan dioptimalkan. Model besar akan dipanggil untuk mengonsumsi token tambahan.',
languageSelectionTip: 'Pilih bahasa yang digunakan dalam percakapan.',
description: 'Description of assistant',
},
setting: {
profile: 'Profil',
@ -748,7 +749,7 @@ export default {
bingTip:
'Komponen ini digunakan untuk mendapatkan hasil pencarian dari https://www.bing.com/. Biasanya, ini berfungsi sebagai pelengkap basis pengetahuan. Top N dan Kunci Langganan Bing menentukan jumlah hasil pencarian yang perlu Anda sesuaikan.',
apiKey: 'Kunci API',
country: 'Negara&Wilayah',
country: 'Negara',
language: 'Bahasa',
googleScholar: 'Google Scholar',
googleScholarDescription:

60
web/src/locales/until.ts Normal file
View File

@ -0,0 +1,60 @@
type NestedObject = {
[key: string]: string | NestedObject;
};
type FlattenedObject = {
[key: string]: string;
};
export function flattenObject(
obj: NestedObject,
parentKey: string = '',
): FlattenedObject {
const result: FlattenedObject = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === 'object' && value !== null) {
Object.assign(result, flattenObject(value as NestedObject, newKey));
} else {
result[newKey] = value as string;
}
}
return result;
}
type TranslationTableRow = {
key: string;
[language: string]: string;
};
/**
* Creates a translation table from multiple flattened language objects.
* @param langs - A list of flattened language objects.
* @param langKeys - A list of language identifiers (e.g., 'English', 'Vietnamese').
* @returns An array representing the translation table.
*/
export function createTranslationTable(
langs: FlattenedObject[],
langKeys: string[],
): TranslationTableRow[] {
const keys = new Set<string>();
// Collect all unique keys from the language objects
langs.forEach((lang) => {
Object.keys(lang).forEach((key) => keys.add(key));
});
// Build the table
return Array.from(keys).map((key) => {
const row: TranslationTableRow = { key };
langs.forEach((lang, index) => {
const langKey = langKeys[index];
row[langKey] = lang[key] || ''; // Use empty string if key is missing
});
return row;
});
}

View File

@ -34,6 +34,7 @@ export default {
pleaseInput: 'Vui lòng nhập',
submit: 'Gửi',
vietnamese: 'Tiếng việt',
spanish: 'Tiếng Tây Ban Nha',
},
login: {
login: 'Đăng nhập',
@ -76,6 +77,7 @@ export default {
namePlaceholder: 'Vui lòng nhập tên!',
doc: 'Tài liệu',
searchKnowledgePlaceholder: 'Tìm kiếm',
noMoreData: 'Tất cả chỉ có thế, không còn gì nữa',
},
knowledgeDetails: {
dataset: 'Dữ liệu',
@ -162,6 +164,7 @@ export default {
autoKeywordsTip: `Trích xuất N từ khóa cho mỗi khối để tăng thứ hạng của chúng cho các truy vấn chứa các từ khóa đó. Bạn có thể kiểm tra hoặc cập nhật các từ khóa đã thêm cho một khối từ danh sách khối. Lưu ý rằng các token bổ sung sẽ được tiêu thụ bởi LLM được chỉ định trong 'Cài đặt mô hình hệ thống'.`,
autoQuestions: 'Câu hỏi tự động',
autoQuestionsTip: `Trích xuất N câu hỏi cho mỗi khối để tăng thứ hạng của chúng cho các truy vấn chứa các câu hỏi đó. Bạn có thể kiểm tra hoặc cập nhật các câu hỏi đã thêm cho một khối từ danh sách khối. Tính năng này sẽ không làm gián đoạn quá trình phân khối nếu xảy ra lỗi, ngoại trừ việc nó có thể thêm kết quả trống vào khối gốc. Lưu ý rằng các token bổ sung sẽ được tiêu thụ bởi LLM được chỉ định trong 'Cài đặt mô hình hệ thống'.`,
delimiterTip: `Hỗ trợ nhiều ký tự phân cách, và các ký tự phân cách nhiều ký tự được bao bọc bởi dấu . Ví dụ: nếu được cấu hình như thế này: "##"; thì văn bản sẽ được phân tách bởi dấu xuống dòng, hai dấu # và dấu chấm phẩy, sau đó được lắp ráp theo kích thước của "số token".`,
},
knowledgeConfiguration: {
titleDescription:
@ -297,6 +300,9 @@ export default {
randomSeed: 'Hạt giống ngẫu nhiên',
randomSeedMessage: 'Hạt giống ngẫu nhiên là bắt buộc',
entityTypes: 'Loại thực thể',
vietnamese: 'Tiếng Việt',
pageRank: 'Xếp hạng trang',
pageRankTip: `Điều này được sử dụng để tăng điểm liên quan. Điểm liên quan với tất cả các khối được truy xuất sẽ cộng với số này, Khi bạn muốn tìm kiếm cơ sở kiến thức đã cho ở vị trí đầu tiên, hãy đặt điểm "Page Rank" cao hơn những điểm khác.`,
},
chunk: {
chunk: 'Khối',
@ -316,6 +322,9 @@ export default {
ellipse: 'Elip',
graph: 'Biểu đồ kiến thức',
mind: 'Sơ đồ tư duy',
question: 'Câu hỏi',
questionTip:
'Nếu có những câu hỏi được đưa ra, việc nhúng phần đó sẽ dựa trên những câu hỏi đó.',
},
chat: {
newConversation: 'Cuộc trò chuyện mới',
@ -593,6 +602,12 @@ export default {
refuse: 'Từ chối',
teamMembers: 'Thành viên nhóm',
joinedTeams: 'Nhóm đã tham gia',
bedrockModelNameMessage: `Vui lòng nhập tên model của bạn!`,
sureDelete: `Bạn có chắc chắn muốn xóa thành viên này không?`,
quit: `Rời khỏi`,
sureQuit: `Bạn có chắc chắn muốn rời khỏi nhóm mà bạn đã tham gia không?`,
FishAudioAKMessage: `Vui lòng nhập KEY API`,
FishAudioRefIDMessage: `Vui lòng nhập ID của model tham chiếu (để trống để sử dụng model mặc định)`,
},
message: {
registered: 'Đã đăng ký!',
@ -625,8 +640,8 @@ export default {
requestError: 'Lỗi yêu cầu',
networkAnomalyDescription:
'Mạng của bạn có sự bất thường và bạn không thể kết nối với máy chủ.',
networkAnomaly: 'bất thường mạng',
hint: 'gợi ý',
networkAnomaly: 'Bất thường mạng',
hint: 'Gợi ý',
},
fileManager: {
name: 'Tên',
@ -1033,6 +1048,27 @@ export default {
optional: 'Tùy chọn',
pasteFileLink: 'Dán liên kết tệp',
testRun: 'Chạy thử nghiệm',
template: 'Mẫu',
templateDescription: `Thành phần này được sử dụng để sắp chữ đầu ra của nhiều thành phần khác nhau.`,
arXivTip: `Thành phần này được sử dụng để lấy kết quả tìm kiếm từ https://arxiv.org/. Thông thường, nó hoạt động như một phần bổ sung cho cơ sở tri thức. Top N chỉ định số lượng kết quả tìm kiếm bạn cần điều chỉnh.`,
googleTip: `Thành phần này được sử dụng để lấy kết quả tìm kiếm từ https://www.google.com/. Thông thường, nó hoạt động như một phần bổ sung cho cơ sở tri thức. Top N và khóa API SerpApi chỉ định số lượng kết quả tìm kiếm bạn cần điều chỉnh.`,
bingTip: `Thành phần này được sử dụng để lấy kết quả tìm kiếm từ https://www.bing.com/. Thông thường, nó hoạt động như một phần bổ sung cho cơ sở tri thức. Top N và khóa đăng ký Bing chỉ định số lượng kết quả tìm kiếm bạn cần điều chỉnh.`,
gitHubDescription: `Thành phần này được sử dụng để tìm kiếm các kho lưu trữ từ https://github.com/. Top N chỉ định số lượng kết quả tìm kiếm cần điều chỉnh.`,
flow: `Quy trình làm việc`,
emailDescription: 'Gửi email đến địa chỉ đã chỉ định',
toEmail: 'Email người nhận',
smtpServerRequired: 'Vui lòng nhập địa chỉ máy chủ SMTP',
emailContent: 'Nội dung',
smtpServer: 'SMTP Server',
smtpPort: 'SMTP Port',
senderEmailRequired: 'Vui lòng nhập email người gửi',
authCodeRequired: 'Vui lòng nhập mã xác thực',
toEmailRequired: 'Vui lòng nhập email người nhận',
emailContentRequired: 'Vui lòng nhập nội dung email',
emailSentSuccess: 'Email đã được gửi thành công',
emailSentFailed: 'Không gửi được email',
jsonFormatTip:
'Thành phần thượng nguồn phải cung cấp chuỗi JSON theo định dạng sau:',
},
footer: {
profile: 'All rights reserved @ React',

View File

@ -428,6 +428,7 @@ export default {
multiTurnTip:
'在多輪對話的中對去知識庫查詢的問題進行最佳化。會呼叫大模型額外消耗token。',
howUseId: '如何使用聊天ID',
description: '助理描述',
},
setting: {
profile: '概述',

View File

@ -29,3 +29,6 @@
.cardSelected {
background-color: @selectedBackgroundColor;
}
.cardSelectedDark {
background-color: #ffffff2f;
}

View File

@ -5,6 +5,7 @@ import classNames from 'classnames';
import DOMPurify from 'dompurify';
import { useEffect, useState } from 'react';
import { useTheme } from '@/components/theme-provider';
import { ChunkTextMode } from '../../constant';
import styles from './index.less';
@ -31,6 +32,7 @@ const ChunkCard = ({
}: IProps) => {
const available = Number(item.available_int);
const [enabled, setEnabled] = useState(false);
const { theme } = useTheme();
const onChange = (checked: boolean) => {
setEnabled(checked);
@ -56,7 +58,8 @@ const ChunkCard = ({
return (
<Card
className={classNames(styles.chunkCard, {
[styles.cardSelected]: selected,
[`${theme === 'dark' ? styles.cardSelectedDark : styles.cardSelected}`]:
selected,
})}
>
<Flex gap={'middle'} justify={'space-between'}>

View File

@ -37,16 +37,14 @@ const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => {
{imageList.length > 0 ? (
<>
<Title level={5} className={styles.topTitle}>
"{item.title}" {t('methodTitle')}
{`"${item.title}" ${t('methodTitle')}`}
</Title>
<p
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(item.description),
}}
></p>
<Title level={5}>
"{item.title}" {t('methodExamples')}
</Title>
<Title level={5}>{`"${item.title}" ${t('methodExamples')}`}</Title>
<Text>{t('methodExamplesDescription')}</Text>
<Row gutter={[10, 10]} className={styles.imageRow}>
{imageList.map((x) => (

View File

@ -70,6 +70,7 @@ const ConfigurationForm = ({ form }: { form: FormInstance }) => {
<Select placeholder={t('languagePlaceholder')}>
<Option value="English">{t('english')}</Option>
<Option value="Chinese">{t('chinese')}</Option>
<Option value="Vietnamese">{t('vietnamese')}</Option>
</Select>
</Form.Item>
<Form.Item

View File

@ -11,7 +11,6 @@
font-size: 16px;
line-height: 24px;
font-weight: @fontWeight700;
color: @gray2;
margin-bottom: 6px;
}
.knowledgeDescription {

View File

@ -1,5 +1,4 @@
.testingWrapper {
flex: 1;
background-color: @grayBackground;
height: 100%;
}

View File

@ -1,6 +1,6 @@
.testingControlWrapper {
width: 350px;
background-color: white;
background-color: rgba(255, 255, 255, 0.1);
padding: 30px 20px;
overflow: auto;
height: calc(100vh - 160px);

View File

@ -1,6 +1,6 @@
.testingResultWrapper {
flex: 1;
background-color: white;
background-color: rgba(255, 255, 255, 0.1);
padding: 30px 20px;
overflow: auto;
height: calc(100vh - 160px);
@ -24,7 +24,7 @@
width: 24px;
height: 24px;
border-radius: 50%;
background-color: rgba(244, 235, 255, 1);
background-color: rgba(255, 255, 255, 0.1);
font-size: 10px;
font-weight: normal;
}

View File

@ -82,7 +82,7 @@ const TestingResult = ({ handleTesting }: IProps) => {
>
<Space>
<span>
{selectedDocumentIds?.length ?? 0}/{documents.length}
{selectedDocumentIds?.length ?? 0}/{documents?.length ?? 0}
</span>
{t('filesSelected')}
</Space>
@ -105,7 +105,7 @@ const TestingResult = ({ handleTesting }: IProps) => {
flex={1}
className={styles.selectFilesCollapse}
>
{chunks.map((x) => (
{chunks?.map((x) => (
<Card key={x.chunk_id} title={<ChunkTitle item={x}></ChunkTitle>}>
<Flex gap={'middle'}>
{x.img_id && (

View File

@ -6,13 +6,13 @@
flex: 1;
overflow-x: auto;
height: 100%;
background-color: rgba(247, 248, 250, 1);
background-color: rgba(255, 255, 255, 0.1);
padding: 16px 20px 28px 40px;
display: flex;
flex-direction: column;
}
.content {
background-color: white;
background-color: rgba(255, 255, 255, 0.1);
margin-top: 16px;
flex: 1;
}

View File

@ -42,224 +42,6 @@ const ModelSetting = ({
})}
>
{visible && <LlmSettingItems prefix="llm_setting"></LlmSettingItems>}
{/* <Form.Item
label={t('model')}
name="llm_id"
tooltip={t('modelTip')}
rules={[{ required: true, message: t('modelMessage') }]}
>
<Select options={modelOptions[LlmModelType.Chat]} showSearch />
</Form.Item>
<Divider></Divider>
<Form.Item
label={t('freedom')}
name="parameters"
tooltip={t('freedomTip')}
initialValue={ModelVariableType.Precise}
>
<Select<ModelVariableType>
options={parameterOptions}
onChange={handleParametersChange}
/>
</Form.Item>
<Form.Item label={t('temperature')} tooltip={t('temperatureTip')}>
<Flex gap={20} align="center">
<Form.Item
name={'temperatureEnabled'}
valuePropName="checked"
noStyle
>
<Switch size="small" />
</Form.Item>
<Form.Item noStyle dependencies={['temperatureEnabled']}>
{({ getFieldValue }) => {
const disabled = !getFieldValue('temperatureEnabled');
return (
<>
<Flex flex={1}>
<Form.Item name={['llm_setting', 'temperature']} noStyle>
<Slider
className={styles.variableSlider}
max={1}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</Flex>
<Form.Item name={['llm_setting', 'temperature']} noStyle>
<InputNumber
className={styles.sliderInputNumber}
max={1}
min={0}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</>
);
}}
</Form.Item>
</Flex>
</Form.Item>
<Form.Item label={t('topP')} tooltip={t('topPTip')}>
<Flex gap={20} align="center">
<Form.Item name={'topPEnabled'} valuePropName="checked" noStyle>
<Switch size="small" />
</Form.Item>
<Form.Item noStyle dependencies={['topPEnabled']}>
{({ getFieldValue }) => {
const disabled = !getFieldValue('topPEnabled');
return (
<>
<Flex flex={1}>
<Form.Item name={['llm_setting', 'top_p']} noStyle>
<Slider
className={styles.variableSlider}
max={1}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</Flex>
<Form.Item name={['llm_setting', 'top_p']} noStyle>
<InputNumber
className={styles.sliderInputNumber}
max={1}
min={0}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</>
);
}}
</Form.Item>
</Flex>
</Form.Item>
<Form.Item label={t('presencePenalty')} tooltip={t('presencePenaltyTip')}>
<Flex gap={20} align="center">
<Form.Item
name={'presencePenaltyEnabled'}
valuePropName="checked"
noStyle
>
<Switch size="small" />
</Form.Item>
<Form.Item noStyle dependencies={['presencePenaltyEnabled']}>
{({ getFieldValue }) => {
const disabled = !getFieldValue('presencePenaltyEnabled');
return (
<>
<Flex flex={1}>
<Form.Item
name={['llm_setting', 'presence_penalty']}
noStyle
>
<Slider
className={styles.variableSlider}
max={1}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</Flex>
<Form.Item name={['llm_setting', 'presence_penalty']} noStyle>
<InputNumber
className={styles.sliderInputNumber}
max={1}
min={0}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</>
);
}}
</Form.Item>
</Flex>
</Form.Item>
<Form.Item
label={t('frequencyPenalty')}
tooltip={t('frequencyPenaltyTip')}
>
<Flex gap={20} align="center">
<Form.Item
name={'frequencyPenaltyEnabled'}
valuePropName="checked"
noStyle
>
<Switch size="small" />
</Form.Item>
<Form.Item noStyle dependencies={['frequencyPenaltyEnabled']}>
{({ getFieldValue }) => {
const disabled = !getFieldValue('frequencyPenaltyEnabled');
return (
<>
<Flex flex={1}>
<Form.Item
name={['llm_setting', 'frequency_penalty']}
noStyle
>
<Slider
className={styles.variableSlider}
max={1}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</Flex>
<Form.Item
name={['llm_setting', 'frequency_penalty']}
noStyle
>
<InputNumber
className={styles.sliderInputNumber}
max={1}
min={0}
step={0.01}
disabled={disabled}
/>
</Form.Item>
</>
);
}}
</Form.Item>
</Flex>
</Form.Item>
<Form.Item label={t('maxTokens')} tooltip={t('maxTokensTip')}>
<Flex gap={20} align="center">
<Form.Item name={'maxTokensEnabled'} valuePropName="checked" noStyle>
<Switch size="small" />
</Form.Item>
<Form.Item noStyle dependencies={['maxTokensEnabled']}>
{({ getFieldValue }) => {
const disabled = !getFieldValue('maxTokensEnabled');
return (
<>
<Flex flex={1}>
<Form.Item name={['llm_setting', 'max_tokens']} noStyle>
<Slider
className={styles.variableSlider}
max={2048}
disabled={disabled}
/>
</Form.Item>
</Flex>
<Form.Item name={['llm_setting', 'max_tokens']} noStyle>
<InputNumber
disabled={disabled}
className={styles.sliderInputNumber}
max={2048}
min={0}
/>
</Form.Item>
</>
);
}}
</Form.Item>
</Flex>
</Form.Item> */}
</section>
);
};

View File

@ -27,6 +27,12 @@
border-radius: 8px;
}
}
.chatAppCardSelectedDark {
:global(.ant-card-body) {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
}
}
.chatTitleWrapper {
width: 220px;
@ -62,6 +68,12 @@
border-radius: 8px;
}
}
.chatTitleCardSelectedDark {
:global(.ant-card-body) {
background-color: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
}
.divider {
margin: 0;

View File

@ -30,6 +30,7 @@ import {
} from './hooks';
import SvgIcon from '@/components/svg-icon';
import { useTheme } from '@/components/theme-provider';
import {
useClickConversationCard,
useClickDialogCard,
@ -51,6 +52,7 @@ const Chat = () => {
const { handleClickDialog } = useClickDialogCard();
const { handleClickConversation } = useClickConversationCard();
const { dialogId, conversationId } = useGetChatSearchParams();
const { theme } = useTheme();
const {
list: conversationList,
addTemporaryConversation,
@ -243,7 +245,9 @@ const Chat = () => {
key={x.id}
hoverable
className={classNames(styles.chatAppCard, {
[styles.chatAppCardSelected]: dialogId === x.id,
[theme === 'dark'
? styles.chatAppCardSelectedDark
: styles.chatAppCardSelected]: dialogId === x.id,
})}
onMouseEnter={handleAppCardEnter(x.id)}
onMouseLeave={handleItemLeave}
@ -316,7 +320,9 @@ const Chat = () => {
onMouseEnter={handleConversationCardEnter(x.id)}
onMouseLeave={handleConversationItemLeave}
className={classNames(styles.chatTitleCard, {
[styles.chatTitleCardSelected]: x.id === conversationId,
[theme === 'dark'
? styles.chatTitleCardSelectedDark
: styles.chatTitleCardSelected]: x.id === conversationId,
})}
>
<Flex justify="space-between" align="center">

View File

@ -13,7 +13,8 @@
:global(.document-container) {
padding: 30px;
width: 700px;
background: white;
background: rgba(255, 255, 255, 0.1);
margin: auto;
}

View File

@ -1,5 +1,5 @@
.contextMenu {
background: white;
background: rgba(255, 255, 255, 0.1);
border-style: solid;
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
position: absolute;
@ -13,6 +13,6 @@
}
button:hover {
background: white;
background: rgba(255, 255, 255, 0.1);
}
}

View File

@ -13,3 +13,19 @@
.edgeButton:hover {
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08);
}
.edgeButtonDark {
width: 14px;
height: 14px;
background: #0e0c0c;
border: 1px solid #fff;
padding: 0;
cursor: pointer;
border-radius: 50%;
font-size: 10px;
line-height: 1;
}
.edgeButtonDark:hover {
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08);
}

View File

@ -6,6 +6,7 @@ import {
} from 'reactflow';
import useGraphStore from '../../store';
import { useTheme } from '@/components/theme-provider';
import { useFetchFlow } from '@/hooks/flow-hooks';
import { useMemo } from 'react';
import styles from './index.less';
@ -33,7 +34,7 @@ export function ButtonEdge({
targetY,
targetPosition,
});
const { theme } = useTheme();
const selectedStyle = useMemo(() => {
return selected ? { strokeWidth: 2, stroke: '#1677ff' } : {};
}, [selected]);
@ -93,7 +94,9 @@ export function ButtonEdge({
className="nodrag nopan"
>
<button
className={styles.edgeButton}
className={
theme === 'dark' ? styles.edgeButtonDark : styles.edgeButton
}
type="button"
onClick={onEdgeClick}
>

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import get from 'lodash/get';
@ -18,12 +19,16 @@ import styles from './index.less';
export function BeginNode({ selected, data }: NodeProps<NodeData>) {
const { t } = useTranslation();
const query: BeginQuery[] = get(data, 'form.query', []);
const { theme } = useTheme();
return (
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.ragNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="source"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -11,12 +12,16 @@ import NodeHeader from './node-header';
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
const { positions } = useBuildCategorizeHandlePositions({ data, id });
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="target"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -17,12 +18,16 @@ export function GenerateNode({
}: NodeProps<NodeData>) {
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
const getLabel = useGetComponentLabelByValue(id);
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -10,9 +10,11 @@
width: 200px;
}
.dark {
background: rgb(63, 63, 63) !important;
}
.ragNode {
.commonNode();
.nodeName {
font-size: 10px;
color: black;
@ -95,7 +97,7 @@
min-width: 140px;
width: auto;
height: 100%;
padding: 0;
padding: 8px;
border-radius: 10px;
min-height: 128px;
.noteTitle {
@ -105,6 +107,13 @@
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.noteTitleDark {
background-color: #edfcff;
font-size: 12px;
padding: 6px 6px 4px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.noteForm {
margin-top: 4px;
height: calc(100% - 50px);

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
@ -11,11 +12,16 @@ export function RagNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.ragNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -15,12 +16,17 @@ export function InvokeNode({
selected,
}: NodeProps<NodeData>) {
const { t } = useTranslation();
const { theme } = useTheme();
const url = get(data, 'form.url');
return (
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.ragNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
@ -13,11 +14,16 @@ export function KeywordNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
@ -11,11 +12,16 @@ export function LogicNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
@ -14,12 +15,16 @@ export function MessageNode({
selected,
}: NodeProps<NodeData>) {
const messages: string[] = get(data, 'form.messages', []);
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -5,6 +5,7 @@ import { NodeData } from '../../interface';
import NodeDropdown from './dropdown';
import SvgIcon from '@/components/svg-icon';
import { useTheme } from '@/components/theme-provider';
import { memo, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
@ -23,6 +24,7 @@ const controlStyle = {
function NoteNode({ data, id }: NodeProps<NodeData>) {
const { t } = useTranslation();
const [form] = Form.useForm();
const { theme } = useTheme();
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
id,
@ -48,10 +50,15 @@ function NoteNode({ data, id }: NodeProps<NodeData>) {
}}
></SvgIcon>
</NodeResizeControl>
<section className={styles.noteNode}>
<section
className={classNames(
styles.noteNode,
theme === 'dark' ? styles.dark : '',
)}
>
<Flex
justify={'space-between'}
className={classNames(styles.noteTitle, 'note-drag-handle')}
className={classNames('note-drag-handle')}
align="center"
gap={6}
>

View File

@ -5,6 +5,7 @@ import JsonView from 'react18-json-view';
import 'react18-json-view/src/style.css';
import { useGetComponentLabelByValue, useReplaceIdWithText } from '../../hooks';
import { useTheme } from '@/components/theme-provider';
import {
Popover,
PopoverContent,
@ -29,6 +30,7 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
const { t } = useTranslate('flow');
const { data } = useFetchFlow();
const { theme } = useTheme();
const component = useMemo(() => {
return get(data, ['dsl', 'components', nodeId], {});
}, [nodeId, data]);
@ -64,7 +66,16 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
<div className="flex w-full gap-4 flex-col">
<div className="flex flex-col space-y-1.5">
<span className="font-semibold text-[14px]">{t('input')}</span>
<div className="bg-gray-100 p-1 rounded">
<div
style={
theme === 'dark'
? {
backgroundColor: 'rgba(150, 150, 150, 0.2)',
}
: {}
}
className={`bg-gray-100 p-1 rounded`}
>
<Table>
<TableHeader>
<TableRow>
@ -85,7 +96,16 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
</div>
<div className="flex flex-col space-y-1.5">
<span className="font-semibold text-[14px]">{t('output')}</span>
<div className="bg-gray-100 p-1 rounded">
<div
style={
theme === 'dark'
? {
backgroundColor: 'rgba(150, 150, 150, 0.2)',
}
: {}
}
className="bg-gray-100 p-1 rounded"
>
<JsonView
src={replacedOutput}
displaySize={30}

View File

@ -4,6 +4,7 @@ import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
import { RightHandleStyle } from './handle-icon';
import { useTheme } from '@/components/theme-provider';
import { get } from 'lodash';
import { useReplaceIdWithName } from '../../hooks';
import styles from './index.less';
@ -13,12 +14,16 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
const yes = get(data, 'form.yes');
const no = get(data, 'form.no');
const replaceIdWithName = useReplaceIdWithName();
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="target"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { UserOutlined } from '@ant-design/icons';
import { Avatar, Flex } from 'antd';
@ -17,6 +18,7 @@ export function RetrievalNode({
selected,
}: NodeProps<NodeData>) {
const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []);
const { theme } = useTheme();
const { list: knowledgeList } = useFetchKnowledgeList(true);
const knowledgeBases = useMemo(() => {
return knowledgeBaseIds.map((x) => {
@ -31,9 +33,13 @@ export function RetrievalNode({
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,4 +1,5 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { useTheme } from '@/components/theme-provider';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
@ -13,11 +14,16 @@ export function RewriteNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,4 @@
import { useTheme } from '@/components/theme-provider';
import { Divider, Flex } from 'antd';
import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
@ -55,12 +56,16 @@ const ConditionBlock = ({
export function SwitchNode({ id, data, selected }: NodeProps<NodeData>) {
const { positions } = useBuildSwitchHandlePositions({ data, id });
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
type="target"

View File

@ -7,6 +7,7 @@ import { IGenerateParameter, NodeData } from '../../interface';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import NodeHeader from './node-header';
import { useTheme } from '@/components/theme-provider';
import styles from './index.less';
export function TemplateNode({
@ -17,12 +18,17 @@ export function TemplateNode({
}: NodeProps<NodeData>) {
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
const getLabel = useGetComponentLabelByValue(id);
const { theme } = useTheme();
return (
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
className={classNames(
styles.logicNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"

View File

@ -1,3 +1,9 @@
import {
GitHubIcon,
KeywordIcon,
QWeatherIcon,
WikipediaIcon,
} from '@/assets/icon/Icon';
import { ReactComponent as AkShareIcon } from '@/assets/svg/akshare.svg';
import { ReactComponent as ArXivIcon } from '@/assets/svg/arxiv.svg';
import { ReactComponent as baiduFanyiIcon } from '@/assets/svg/baidu-fanyi.svg';
@ -10,20 +16,16 @@ import { ReactComponent as DeepLIcon } from '@/assets/svg/deepl.svg';
import { ReactComponent as DuckIcon } from '@/assets/svg/duck.svg';
import { ReactComponent as EmailIcon } from '@/assets/svg/email.svg';
import { ReactComponent as ExeSqlIcon } from '@/assets/svg/exesql.svg';
import { ReactComponent as GithubIcon } from '@/assets/svg/github.svg';
import { ReactComponent as GoogleScholarIcon } from '@/assets/svg/google-scholar.svg';
import { ReactComponent as GoogleIcon } from '@/assets/svg/google.svg';
import { ReactComponent as InvokeIcon } from '@/assets/svg/invoke-ai.svg';
import { ReactComponent as Jin10Icon } from '@/assets/svg/jin10.svg';
import { ReactComponent as KeywordIcon } from '@/assets/svg/keyword.svg';
import { ReactComponent as NoteIcon } from '@/assets/svg/note.svg';
import { ReactComponent as PubMedIcon } from '@/assets/svg/pubmed.svg';
import { ReactComponent as QWeatherIcon } from '@/assets/svg/qweather.svg';
import { ReactComponent as SwitchIcon } from '@/assets/svg/switch.svg';
import { ReactComponent as TemplateIcon } from '@/assets/svg/template.svg';
import { ReactComponent as TuShareIcon } from '@/assets/svg/tushare.svg';
import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg';
import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg';
import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.svg';
// 邮件功能
@ -116,7 +118,7 @@ export const operatorIconMap = {
[Operator.Bing]: BingIcon,
[Operator.GoogleScholar]: GoogleScholarIcon,
[Operator.DeepL]: DeepLIcon,
[Operator.GitHub]: GithubIcon,
[Operator.GitHub]: GitHubIcon,
[Operator.BaiduFanyi]: baiduFanyiIcon,
[Operator.QWeather]: QWeatherIcon,
[Operator.ExeSQL]: ExeSqlIcon,
@ -193,11 +195,10 @@ export const operatorMap: Record<
[Operator.KeywordExtract]: {
width: 70,
height: 70,
backgroundColor: '#0f0e0f',
color: '#0f0e0f',
backgroundColor: '#6E5494',
color: '#6E5494',
fontSize: 12,
iconWidth: 16,
// iconFontSize: 16,
},
[Operator.DuckDuckGo]: {
backgroundColor: '#e7e389',
@ -235,10 +236,14 @@ export const operatorMap: Record<
backgroundColor: '#f5e8e6',
},
[Operator.GitHub]: {
backgroundColor: '#c7c7f8',
backgroundColor: 'purple',
color: 'purple',
},
[Operator.BaiduFanyi]: { backgroundColor: '#e5f2d3' },
[Operator.QWeather]: { backgroundColor: '#a4bbf3' },
[Operator.QWeather]: {
backgroundColor: '#a4bbf3',
color: '#a4bbf3',
},
[Operator.ExeSQL]: { backgroundColor: '#b9efe8' },
[Operator.Switch]: { backgroundColor: '#dbaff6', color: '#dbaff6' },
[Operator.WenCai]: { backgroundColor: '#faac5b' },

View File

@ -1,7 +1,7 @@
.dynamicInputVariable {
background-color: #ebe9e9;
background-color: #ebe9e950;
:global(.ant-collapse-content) {
background-color: #f6f6f6;
background-color: #f6f6f657;
}
:global(.ant-collapse-content-box) {
padding: 0 !important;

View File

@ -1,7 +1,7 @@
.dynamicInputVariable {
background-color: #ebe9e9;
background-color: #ebe9e950;
:global(.ant-collapse-content) {
background-color: #f6f6f6;
background-color: #f6f6f657;
}
margin-bottom: 20px;
.title {

View File

@ -18,9 +18,9 @@
}
.dynamicParameterVariable {
background-color: #ebe9e9;
background-color: #ebe9e950;
:global(.ant-collapse-content) {
background-color: #f6f6f6;
background-color: #f6f6f634;
}
:global(.ant-collapse-content-box) {
padding: 0 !important;

View File

@ -27,7 +27,8 @@
.card {
border-radius: 12px;
border: 1px solid rgba(234, 236, 240, 1);
border: 1px solid rgba(0, 0, 0, 0.3);
background-color: rgba(255, 255, 255, 0.1);
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
padding: 24px;
width: 300px;
@ -39,14 +40,12 @@
font-size: 24px;
line-height: 32px;
font-weight: 600;
color: rgba(0, 0, 0, 0.88);
word-break: break-all;
}
.description {
font-size: 12px;
font-weight: 600;
line-height: 20px;
color: rgba(0, 0, 0, 0.45);
}
}
@ -72,7 +71,6 @@
.rightText {
font-size: 12px;
font-weight: 600;
color: rgba(0, 0, 0, 0.65);
vertical-align: middle;
}
}

View File

@ -17,7 +17,7 @@
font-style: normal;
font-weight: @fontWeight600;
line-height: 38px;
color: rgba(16, 24, 40, 1);
color: var(--ant-color-info);
}
.description {
font-family: Inter;
@ -25,7 +25,6 @@
font-style: normal;
font-weight: 400;
line-height: 24px;
color: rgba(71, 84, 103, 1);
}
.topButton {

View File

@ -51,31 +51,50 @@
line-height: 20px;
color: rgba(0, 0, 0, 0.45);
}
.titledark {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
font-size: 24px;
line-height: 32px;
font-weight: 600;
word-break: break-all;
}
.descriptiondark {
margin-top: 4px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
font-size: 12px;
font-weight: 600;
line-height: 20px;
}
}
}
.card {
border-radius: 12px;
border: 1px solid rgba(234, 236, 240, 1);
border: 1px solid rgba(0, 0, 0, 0.3);
background-color: rgba(255, 255, 255, 0.1);
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
padding: 24px;
width: 300px;
cursor: pointer;
.titleWrapper {
// flex: 1;
.title {
font-size: 24px;
line-height: 32px;
font-weight: 600;
color: rgba(0, 0, 0, 0.88);
word-break: break-all;
}
.description {
font-size: 12px;
font-weight: 600;
line-height: 20px;
color: rgba(0, 0, 0, 0.45);
}
}
@ -101,7 +120,6 @@
.rightText {
font-size: 12px;
font-weight: 600;
color: rgba(0, 0, 0, 0.65);
vertical-align: middle;
}
}

View File

@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next';
import { useNavigate } from 'umi';
import OperateDropdown from '@/components/operate-dropdown';
import { useTheme } from '@/components/theme-provider';
import { useDeleteKnowledge } from '@/hooks/knowledge-hooks';
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import styles from './index.less';
@ -24,7 +25,7 @@ const KnowledgeCard = ({ item }: IProps) => {
const navigate = useNavigate();
const { t } = useTranslation();
const { data: userInfo } = useFetchUserInfo();
const { theme } = useTheme();
const { deleteKnowledge } = useDeleteKnowledge();
const removeKnowledge = async () => {
@ -52,8 +53,18 @@ const KnowledgeCard = ({ item }: IProps) => {
<OperateDropdown deleteItem={removeKnowledge}></OperateDropdown>
</div>
<div className={styles.titleWrapper}>
<span className={styles.title}>{item.name}</span>
<p className={styles.description}>{item.description}</p>
<span
className={theme === 'dark' ? styles.titledark : styles.title}
>
{item.name}
</span>
<p
className={
theme === 'dark' ? styles.descriptiondark : styles.description
}
>
{item.description}
</p>
</div>
<div className={styles.footer}>
<div className={styles.footerTop}>

View File

@ -2,10 +2,12 @@
.loginPage {
display: flex;
background-color: rgba(255, 255, 255, 0.1);
.loginLeft {
// width: 610px;
width: 40%;
background-color: #fff;
background-color: rgba(255, 255, 255, 0.1);
height: 100vh;
display: flex;
align-items: center;
@ -39,7 +41,6 @@
z-index: -1;
}
.white {
color: #fff;
}
.pink {
color: #e9d7fe;
@ -87,15 +88,12 @@
span {
font-size: 16px;
line-height: 24px;
color: #000000a6;
}
}
@media screen and (max-width: 957px) {
.loginLeft {
width: 100%;
background-color: #fff;
height: 100%;
}

View File

@ -15,16 +15,8 @@
font-size: 14px;
cursor: pointer;
}
// .mainLayout {
// background: transparent;
// }
}
// .transparentSearchSide {
// background-color: rgb(251 251 251 / 88%) !important;
// }
.searchSide {
position: relative;
max-width: 400px !important;
@ -101,10 +93,10 @@
}
.answerWrapper {
margin-top: 16px;
background: rgb(232 242 251 / 70%);
background: rgba(232 242 251, 1);
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.08);
:global(.ant-card-head) {
background-color: #e6f4ff;
background-color: #e6f4ff23;
}
& p {
margin: 0;

View File

@ -146,9 +146,9 @@ const SearchPage = () => {
></RetrievalDocuments>
<Divider></Divider>
<Spin spinning={loading}>
{chunks.length > 0 && (
{chunks?.length > 0 && (
<List
dataSource={chunks}
dataSource={chunks || []}
className={styles.chunks}
renderItem={(item) => (
<List.Item>

View File

@ -1,19 +1,21 @@
import { ReactComponent as ApiIcon } from '@/assets/svg/api.svg';
import { ReactComponent as LogoutIcon } from '@/assets/svg/logout.svg';
import { ReactComponent as ModelIcon } from '@/assets/svg/model-providers.svg';
import { ReactComponent as PasswordIcon } from '@/assets/svg/password.svg';
import { ReactComponent as ProfileIcon } from '@/assets/svg/profile.svg';
import { ReactComponent as TeamIcon } from '@/assets/svg/team.svg';
import {
ApiIcon,
LogOutIcon,
ModelProviderIcon,
PasswordIcon,
ProfileIcon,
TeamIcon,
} from '@/assets/icon/Icon';
import { UserSettingRouteKey } from '@/constants/setting';
import { MonitorOutlined } from '@ant-design/icons';
export const UserSettingIconMap = {
[UserSettingRouteKey.Profile]: <ProfileIcon />,
[UserSettingRouteKey.Password]: <PasswordIcon />,
[UserSettingRouteKey.Model]: <ModelIcon />,
[UserSettingRouteKey.Model]: <ModelProviderIcon />,
[UserSettingRouteKey.System]: <MonitorOutlined style={{ fontSize: 24 }} />,
[UserSettingRouteKey.Team]: <TeamIcon />,
[UserSettingRouteKey.Logout]: <LogoutIcon />,
[UserSettingRouteKey.Logout]: <LogOutIcon />,
[UserSettingRouteKey.Api]: <ApiIcon />,
};

View File

@ -0,0 +1,68 @@
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import React from 'react';
type TranslationTableRow = {
key: string;
[language: string]: string;
};
interface TranslationTableProps {
data: TranslationTableRow[];
languages: string[];
}
const TranslationTable: React.FC<TranslationTableProps> = ({
data,
languages,
}) => {
// Define columns dynamically based on languages
const columns: ColumnsType<TranslationTableRow> = [
{
title: 'Key',
dataIndex: 'key',
key: 'key',
fixed: 'left',
width: 200,
sorter: (a, b) => a.key.localeCompare(b.key), // Sorting by key
},
...languages.map((lang) => ({
title: lang,
dataIndex: lang,
key: lang,
sorter: (a: any, b: any) => a[lang].localeCompare(b[lang]), // Sorting by language
// Example filter for each language
filters: [
{
text: 'Show Empty',
value: 'show_empty',
},
{
text: 'Show Non-Empty',
value: 'show_non_empty',
},
],
onFilter: (value: any, record: any) => {
if (value === 'show_empty') {
return !record[lang]; // Show rows with empty translations
}
if (value === 'show_non_empty') {
return record[lang] && record[lang].length > 0; // Show rows with non-empty translations
}
return true;
},
})),
];
return (
<Table
columns={columns}
dataSource={data}
rowKey="key"
pagination={{ pageSize: 10 }}
scroll={{ x: true }}
/>
);
};
export default TranslationTable;

View File

@ -0,0 +1,13 @@
import { translationTable } from '@/locales/config';
import TranslationTable from './TranslationTable';
function UserSettingLocale() {
return (
<TranslationTable
data={translationTable}
languages={['English', 'Vietnamese', 'Spanish', 'zh', 'zh-TRADITIONAL']}
/>
);
}
export default UserSettingLocale;

View File

@ -24,12 +24,30 @@
padding: 0;
}
}
.toBeAddedCardDark {
border-radius: 24px;
border: 1px solid #eaecf0;
background: #e3f0ff2a;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
:global(.ant-card-body) {
padding: 10px 24px;
}
.addButton {
padding: 0;
}
}
.addedCard {
border-radius: 18px;
border: 1px solid #eaecf0;
background: #e6e7eb;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
}
.addedCardDark {
border-radius: 18px;
border: 1px solid #eaecf0;
background: #e6e7eb21;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
}
.modelDivider {
margin: 0;
}

View File

@ -1,5 +1,6 @@
import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg';
import { LlmIcon } from '@/components/svg-icon';
import { useTheme } from '@/components/theme-provider';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { LlmItem, useSelectLlmList } from '@/hooks/llm-hooks';
import { CloseCircleOutlined, SettingOutlined } from '@ant-design/icons';
@ -61,6 +62,7 @@ interface IModelCardProps {
const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
const { visible, switchVisible } = useSetModalState();
const { t } = useTranslate('setting');
const { theme } = useTheme();
const { handleDeleteLlm } = useHandleDeleteLlm(item.name);
const { handleDeleteFactory } = useHandleDeleteFactory(item.name);
@ -74,7 +76,9 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
return (
<List.Item>
<Card className={styles.addedCard}>
<Card
className={theme === 'dark' ? styles.addedCardDark : styles.addedCard}
>
<Row align={'middle'}>
<Col span={12}>
<Flex gap={'middle'} align="center">
@ -139,6 +143,7 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
const UserSettingModel = () => {
const { factoryList, myLlmList: llmList, loading } = useSelectLlmList();
const { theme } = useTheme();
const {
saveApiKeyLoading,
initialApiKey,
@ -313,7 +318,13 @@ const UserSettingModel = () => {
dataSource={factoryList}
renderItem={(item) => (
<List.Item>
<Card className={styles.toBeAddedCard}>
<Card
className={
theme === 'dark'
? styles.toBeAddedCardDark
: styles.toBeAddedCard
}
>
<Flex vertical gap={'middle'}>
<LlmIcon name={item.name} />
<Flex vertical gap={'middle'}>

View File

@ -77,6 +77,10 @@ const routes = [
path: '/user-setting/profile',
component: '@/pages/user-setting/setting-profile',
},
{
path: '/user-setting/locale',
component: '@/pages/user-setting/setting-locale',
},
{
path: '/user-setting/password',
component: '@/pages/user-setting/setting-password',