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
85 changed files with 1041 additions and 520 deletions

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'}>