mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-23 03:26:53 +08:00
Refactor: Replace antd with shadcn (#12718)
### What problem does this PR solve? Refactor: Replace antd with shadcn ### Type of change - [x] Refactoring
This commit is contained in:
@ -11,8 +11,8 @@ import {
|
||||
} from '@/hooks/use-user-setting-request';
|
||||
import { IStats } from '@/interfaces/database/chat';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { message } from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
import message from '../ui/message';
|
||||
|
||||
export const useOperateApiKey = (idKey: string, dialogId?: string) => {
|
||||
const { removeToken } = useRemoveSystemToken();
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import { CopyToClipboard as Clipboard, Props } from 'react-copy-to-clipboard';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||
|
||||
const CopyToClipboard = ({ text }: Props) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
@ -16,10 +16,13 @@ const CopyToClipboard = ({ text }: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip title={copied ? t('copied') : t('copy')}>
|
||||
<Clipboard text={text} onCopy={handleCopy}>
|
||||
{copied ? <CheckOutlined /> : <CopyOutlined />}
|
||||
</Clipboard>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Clipboard text={text} onCopy={handleCopy}>
|
||||
{copied ? <CheckOutlined /> : <CopyOutlined />}
|
||||
</Clipboard>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{copied ? t('copied') : t('copy')}</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
import { Form, FormInstance, Input, InputRef, Typography } from 'antd';
|
||||
import { omit } from 'lodash';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
const EditableContext = React.createContext<FormInstance<any> | null>(null);
|
||||
const { Text } = Typography;
|
||||
|
||||
interface EditableRowProps {
|
||||
index: number;
|
||||
}
|
||||
|
||||
interface Item {
|
||||
key: string;
|
||||
name: string;
|
||||
age: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export const EditableRow: React.FC<EditableRowProps> = ({ ...props }) => {
|
||||
const [form] = Form.useForm();
|
||||
return (
|
||||
<Form form={form} component={false}>
|
||||
<EditableContext.Provider value={form}>
|
||||
<tr {...omit(props, 'index')} />
|
||||
</EditableContext.Provider>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
interface EditableCellProps {
|
||||
title: React.ReactNode;
|
||||
editable: boolean;
|
||||
children: React.ReactNode;
|
||||
dataIndex: keyof Item;
|
||||
record: Item;
|
||||
handleSave: (record: Item) => void;
|
||||
}
|
||||
|
||||
export const EditableCell: React.FC<EditableCellProps> = ({
|
||||
title,
|
||||
editable,
|
||||
children,
|
||||
dataIndex,
|
||||
record,
|
||||
handleSave,
|
||||
...restProps
|
||||
}) => {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const form = useContext(EditableContext)!;
|
||||
|
||||
useEffect(() => {
|
||||
if (editing) {
|
||||
inputRef.current!.focus();
|
||||
}
|
||||
}, [editing]);
|
||||
|
||||
const toggleEdit = () => {
|
||||
setEditing(!editing);
|
||||
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
|
||||
toggleEdit();
|
||||
handleSave({ ...record, ...values });
|
||||
} catch (errInfo) {
|
||||
console.log('Save failed:', errInfo);
|
||||
}
|
||||
};
|
||||
|
||||
let childNode = children;
|
||||
|
||||
if (editable) {
|
||||
childNode = editing ? (
|
||||
<Form.Item
|
||||
style={{ margin: 0, minWidth: 70 }}
|
||||
name={dataIndex}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: `${title} is required.`,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div onClick={toggleEdit} className="editable-cell-value-wrap">
|
||||
<Text>{children}</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <td {...restProps}>{childNode}</td>;
|
||||
};
|
||||
@ -15,12 +15,12 @@ import {
|
||||
} from '@/utils/chat';
|
||||
import { getExtension } from '@/utils/document-util';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Button, Flex, Popover, Tooltip } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import DOMPurify from 'dompurify';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import { omit } from 'lodash';
|
||||
import { pipe } from 'lodash/fp';
|
||||
import { Info } from 'lucide-react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Markdown from 'react-markdown';
|
||||
@ -37,6 +37,9 @@ import remarkMath from 'remark-math';
|
||||
import { visitParents } from 'unist-util-visit-parents';
|
||||
import styles from './floating-chat-widget-markdown.module.less';
|
||||
import { useIsDarkTheme } from './theme-provider';
|
||||
import { Button } from './ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from './ui/popover';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||
|
||||
const getChunkIndex = (match: string) => Number(match.replace(/\[|\]/g, ''));
|
||||
|
||||
@ -161,19 +164,19 @@ const FloatingChatWidgetMarkdown = ({
|
||||
className="flex gap-2 widget-citation-content"
|
||||
>
|
||||
{imageId && (
|
||||
<Popover
|
||||
placement="left"
|
||||
content={
|
||||
<Popover>
|
||||
<TooltipTrigger asChild>
|
||||
<Image
|
||||
id={imageId}
|
||||
className="w-24 h-24 object-contain rounded m-1 cursor-pointer"
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="left">
|
||||
<Image
|
||||
id={imageId}
|
||||
className="max-w-[80vw] max-h-[60vh] rounded"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Image
|
||||
id={imageId}
|
||||
className="w-24 h-24 object-contain rounded m-1 cursor-pointer"
|
||||
/>
|
||||
</TooltipContent>
|
||||
</Popover>
|
||||
)}
|
||||
<div className="space-y-2 flex-1 min-w-0">
|
||||
@ -184,7 +187,7 @@ const FloatingChatWidgetMarkdown = ({
|
||||
className="max-h-[250px] overflow-y-auto text-xs leading-relaxed p-2 bg-gray-50 dark:bg-gray-800 rounded prose-sm"
|
||||
></div>
|
||||
{documentId && (
|
||||
<Flex gap={'small'} align="center">
|
||||
<section className="flex gap-1 justify-center">
|
||||
{fileThumbnail ? (
|
||||
<img
|
||||
src={fileThumbnail}
|
||||
@ -194,32 +197,33 @@ const FloatingChatWidgetMarkdown = ({
|
||||
) : (
|
||||
<SvgIcon name={`file-icon/${fileExtension}`} width={20} />
|
||||
)}
|
||||
<Tooltip
|
||||
title={
|
||||
!documentUrl && fileExtension !== 'pdf'
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
size={'sm'}
|
||||
variant={'link'}
|
||||
className="p-0 text-xs break-words h-auto text-left flex-1"
|
||||
onClick={handleDocumentButtonClick(
|
||||
documentId,
|
||||
chunkItem,
|
||||
fileExtension === 'pdf',
|
||||
documentUrl,
|
||||
)}
|
||||
disabled={!documentUrl && fileExtension !== 'pdf'}
|
||||
style={{ whiteSpace: 'normal' }}
|
||||
>
|
||||
<span className="truncate">
|
||||
{document?.doc_name ?? 'Unnamed Document'}
|
||||
</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{!documentUrl && fileExtension !== 'pdf'
|
||||
? 'Document link unavailable'
|
||||
: document.doc_name
|
||||
}
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
className="p-0 text-xs break-words h-auto text-left flex-1"
|
||||
onClick={handleDocumentButtonClick(
|
||||
documentId,
|
||||
chunkItem,
|
||||
fileExtension === 'pdf',
|
||||
documentUrl,
|
||||
)}
|
||||
disabled={!documentUrl && fileExtension !== 'pdf'}
|
||||
style={{ whiteSpace: 'normal' }}
|
||||
>
|
||||
<span className="truncate">
|
||||
{document?.doc_name ?? 'Unnamed Document'}
|
||||
</span>
|
||||
</Button>
|
||||
: document.doc_name}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@ -236,8 +240,11 @@ const FloatingChatWidgetMarkdown = ({
|
||||
|
||||
if (!info) {
|
||||
return (
|
||||
<Tooltip key={`err-tooltip-${i}`} title="Reference unavailable">
|
||||
<InfoCircleOutlined className={styles.referenceIcon} />
|
||||
<Tooltip key={`err-tooltip-${i}`}>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className={styles.referenceIcon} />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Reference unavailable</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@ -262,8 +269,11 @@ const FloatingChatWidgetMarkdown = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover content={getPopoverContent(chunkIndex)} key={`popover-${i}`}>
|
||||
<InfoCircleOutlined className={styles.referenceIcon} />
|
||||
<Popover key={`popover-${i}`}>
|
||||
<PopoverTrigger asChild>
|
||||
<InfoCircleOutlined className={styles.referenceIcon} />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>{getPopoverContent(chunkIndex)}</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import IndentedTree from './indented-tree';
|
||||
|
||||
import { useFetchKnowledgeGraph } from '@/hooks/use-knowledge-request';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { Modal } from 'antd';
|
||||
|
||||
const IndentedTreeModal = ({
|
||||
visible,
|
||||
hideModal,
|
||||
}: IModalProps<any> & { documentId: string }) => {
|
||||
const { data } = useFetchKnowledgeGraph();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t('chunk.mind')}
|
||||
open={visible}
|
||||
onCancel={hideModal}
|
||||
width={'90vw'}
|
||||
footer={null}
|
||||
>
|
||||
<section>
|
||||
<IndentedTree data={data?.mind_map} show></IndentedTree>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndentedTreeModal;
|
||||
@ -1,67 +0,0 @@
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
||||
import { Popover as AntPopover, Select as AntSelect } from 'antd';
|
||||
import LlmSettingItems from '../llm-setting-items';
|
||||
|
||||
interface IProps {
|
||||
id?: string;
|
||||
value?: string;
|
||||
onInitialValue?: (value: string, option: any) => void;
|
||||
onChange?: (value: string, option: any) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const LLMSelect = ({
|
||||
id,
|
||||
value,
|
||||
onInitialValue,
|
||||
onChange,
|
||||
disabled,
|
||||
}: IProps) => {
|
||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||
LlmModelType.Chat,
|
||||
LlmModelType.Image2text,
|
||||
]);
|
||||
|
||||
if (onInitialValue && value) {
|
||||
for (const modelOption of modelOptions) {
|
||||
for (const option of modelOption.options) {
|
||||
if (option.value === value) {
|
||||
onInitialValue(value, option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const content = (
|
||||
<div style={{ width: 400 }}>
|
||||
<LlmSettingItems
|
||||
onChange={onChange}
|
||||
formItemLayout={{ labelCol: { span: 10 }, wrapperCol: { span: 14 } }}
|
||||
></LlmSettingItems>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<AntPopover
|
||||
content={content}
|
||||
trigger="click"
|
||||
placement="left"
|
||||
arrow={false}
|
||||
destroyTooltipOnHide
|
||||
>
|
||||
<AntSelect
|
||||
options={modelOptions}
|
||||
style={{ width: '100%' }}
|
||||
dropdownStyle={{ display: 'none' }}
|
||||
id={id}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</AntPopover>
|
||||
);
|
||||
};
|
||||
|
||||
export default LLMSelect;
|
||||
@ -1,350 +0,0 @@
|
||||
import {
|
||||
LlmModelType,
|
||||
ModelVariableType,
|
||||
settledModelVariableMap,
|
||||
} from '@/constants/knowledge';
|
||||
import { Flex, Form, InputNumber, Select, Slider, Switch, Tooltip } from 'antd';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
||||
import { setChatVariableEnabledFieldValuePage } from '@/utils/chat';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import styles from './index.module.less';
|
||||
|
||||
interface IProps {
|
||||
prefix?: string;
|
||||
formItemLayout?: any;
|
||||
handleParametersChange?(value: ModelVariableType): void;
|
||||
onChange?(value: string, option: any): void;
|
||||
}
|
||||
|
||||
const LlmSettingItems = ({ prefix, formItemLayout = {}, onChange }: IProps) => {
|
||||
const form = Form.useFormInstance();
|
||||
const { t } = useTranslate('chat');
|
||||
const parameterOptions = Object.values(ModelVariableType).map((x) => ({
|
||||
label: t(camelCase(x)),
|
||||
value: x,
|
||||
}));
|
||||
|
||||
const handleParametersChange = useCallback(
|
||||
(value: ModelVariableType) => {
|
||||
const variable = settledModelVariableMap[value];
|
||||
let nextVariable: Record<string, any> = variable;
|
||||
if (prefix) {
|
||||
nextVariable = { [prefix]: variable };
|
||||
}
|
||||
const variableCheckBoxFieldMap = setChatVariableEnabledFieldValuePage();
|
||||
form.setFieldsValue({ ...nextVariable, ...variableCheckBoxFieldMap });
|
||||
},
|
||||
[form, prefix],
|
||||
);
|
||||
|
||||
const memorizedPrefix = useMemo(() => (prefix ? [prefix] : []), [prefix]);
|
||||
|
||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||
LlmModelType.Chat,
|
||||
LlmModelType.Image2text,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label={t('model')}
|
||||
name="llm_id"
|
||||
tooltip={t('modelTip')}
|
||||
{...formItemLayout}
|
||||
rules={[{ required: true, message: t('modelMessage') }]}
|
||||
>
|
||||
<Select
|
||||
options={modelOptions}
|
||||
showSearch
|
||||
popupMatchSelectWidth={false}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="border rounded-md">
|
||||
<div className="flex justify-between bg-slate-100 p-2 mb-2">
|
||||
<div className="space-x-1 items-center">
|
||||
<span className="text-lg font-semibold">{t('freedom')}</span>
|
||||
<Tooltip title={t('freedomTip')}>
|
||||
<QuestionCircleOutlined></QuestionCircleOutlined>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="w-1/4 min-w-32">
|
||||
<Form.Item
|
||||
label={t('freedom')}
|
||||
name="parameter"
|
||||
tooltip={t('freedomTip')}
|
||||
initialValue={ModelVariableType.Precise}
|
||||
labelCol={{ span: 0 }}
|
||||
wrapperCol={{ span: 24 }}
|
||||
className="m-0"
|
||||
>
|
||||
<Select<ModelVariableType>
|
||||
options={parameterOptions}
|
||||
onChange={handleParametersChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pr-2">
|
||||
<Form.Item
|
||||
label={t('temperature')}
|
||||
tooltip={t('temperatureTip')}
|
||||
{...formItemLayout}
|
||||
>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item
|
||||
name={'temperatureEnabled'}
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
>
|
||||
<Switch size="small" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
dependencies={['temperatureEnabled']}
|
||||
shouldUpdate
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const disabled = !getFieldValue('temperatureEnabled');
|
||||
return (
|
||||
<>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, 'temperature']}
|
||||
noStyle
|
||||
>
|
||||
<Slider
|
||||
className={styles.variableSlider}
|
||||
max={1}
|
||||
step={0.01}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, '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')}
|
||||
{...formItemLayout}
|
||||
>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item name={'topPEnabled'} valuePropName="checked" noStyle>
|
||||
<Switch size="small" />
|
||||
</Form.Item>
|
||||
<Form.Item noStyle dependencies={['topPEnabled']} shouldUpdate>
|
||||
{({ getFieldValue }) => {
|
||||
const disabled = !getFieldValue('topPEnabled');
|
||||
return (
|
||||
<>
|
||||
<Flex flex={1}>
|
||||
<Form.Item name={[...memorizedPrefix, 'top_p']} noStyle>
|
||||
<Slider
|
||||
className={styles.variableSlider}
|
||||
max={1}
|
||||
step={0.01}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item name={[...memorizedPrefix, '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')}
|
||||
{...formItemLayout}
|
||||
>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item
|
||||
name={'presencePenaltyEnabled'}
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
>
|
||||
<Switch size="small" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
dependencies={['presencePenaltyEnabled']}
|
||||
shouldUpdate
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const disabled = !getFieldValue('presencePenaltyEnabled');
|
||||
return (
|
||||
<>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, 'presence_penalty']}
|
||||
noStyle
|
||||
>
|
||||
<Slider
|
||||
className={styles.variableSlider}
|
||||
max={1}
|
||||
step={0.01}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, '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')}
|
||||
{...formItemLayout}
|
||||
>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item
|
||||
name={'frequencyPenaltyEnabled'}
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
>
|
||||
<Switch size="small" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
dependencies={['frequencyPenaltyEnabled']}
|
||||
shouldUpdate
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const disabled = !getFieldValue('frequencyPenaltyEnabled');
|
||||
return (
|
||||
<>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, 'frequency_penalty']}
|
||||
noStyle
|
||||
>
|
||||
<Slider
|
||||
className={styles.variableSlider}
|
||||
max={1}
|
||||
step={0.01}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, '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')}
|
||||
{...formItemLayout}
|
||||
>
|
||||
<Flex gap={20} align="center">
|
||||
<Form.Item
|
||||
name={'maxTokensEnabled'}
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
>
|
||||
<Switch size="small" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
dependencies={['maxTokensEnabled']}
|
||||
shouldUpdate
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const disabled = !getFieldValue('maxTokensEnabled');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex flex={1}>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, 'max_tokens']}
|
||||
noStyle
|
||||
>
|
||||
<Slider
|
||||
className={styles.variableSlider}
|
||||
max={128000}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name={[...memorizedPrefix, 'max_tokens']}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
disabled={disabled}
|
||||
className={styles.sliderInputNumber}
|
||||
max={128000}
|
||||
min={0}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LlmSettingItems;
|
||||
@ -1,11 +1,10 @@
|
||||
import { IconMap, LLMFactory } from '@/constants/llm';
|
||||
import { cn } from '@/lib/utils';
|
||||
import Icon, { UserOutlined } from '@ant-design/icons';
|
||||
import Icon from '@ant-design/icons';
|
||||
import { IconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
||||
import { Avatar } from 'antd';
|
||||
import { AvatarSize } from 'antd/es/avatar/AvatarContext';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { IconFontFill } from './icon-font';
|
||||
import { RAGFlowAvatar } from './ragflow-avatar';
|
||||
import { useIsDarkTheme } from './theme-provider';
|
||||
|
||||
// const importAll = (requireContext: __WebpackModuleApi.RequireContext) => {
|
||||
@ -91,13 +90,11 @@ export const LlmIcon = ({
|
||||
name,
|
||||
height = 48,
|
||||
width = 48,
|
||||
size = 'large',
|
||||
imgClass,
|
||||
}: {
|
||||
name: string;
|
||||
height?: number;
|
||||
width?: number;
|
||||
size?: AvatarSize;
|
||||
imgClass?: string;
|
||||
}) => {
|
||||
const isDark = useIsDarkTheme();
|
||||
@ -134,7 +131,6 @@ export const LlmIcon = ({
|
||||
name={'moxing-default'}
|
||||
className={cn('size-8 flex items-center justify-center', imgClass)}
|
||||
/>
|
||||
// <Avatar shape="square" size={size} icon={<UserOutlined />} />
|
||||
);
|
||||
};
|
||||
|
||||
@ -142,13 +138,11 @@ export const HomeIcon = ({
|
||||
name,
|
||||
height = '32',
|
||||
width = '32',
|
||||
size = 'large',
|
||||
imgClass,
|
||||
}: {
|
||||
name: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
size?: AvatarSize;
|
||||
imgClass?: string;
|
||||
}) => {
|
||||
const isDark = useIsDarkTheme();
|
||||
@ -162,7 +156,7 @@ export const HomeIcon = ({
|
||||
imgClass={imgClass}
|
||||
></SvgIcon>
|
||||
) : (
|
||||
<Avatar shape="square" size={size} icon={<UserOutlined />} />
|
||||
<RAGFlowAvatar avatar={'user'}></RAGFlowAvatar>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
import { Divider, Layout, theme } from 'antd';
|
||||
import React from 'react';
|
||||
import { Outlet } from 'react-router';
|
||||
import '../locales/config';
|
||||
import Header from './components/header';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
const App: React.FC = () => {
|
||||
const {
|
||||
token: { colorBgContainer, borderRadiusLG },
|
||||
} = theme.useToken();
|
||||
|
||||
return (
|
||||
<Layout className={styles.layout}>
|
||||
<Layout>
|
||||
<Header></Header>
|
||||
<Divider orientationMargin={0} className={styles.divider} />
|
||||
<Content
|
||||
style={{
|
||||
minHeight: 280,
|
||||
background: colorBgContainer,
|
||||
borderRadius: borderRadiusLG,
|
||||
overflow: 'auto',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<Outlet />
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@ -1,223 +0,0 @@
|
||||
import FilterIcon from '@/assets/filter.svg';
|
||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import {
|
||||
IChunkListResult,
|
||||
useSelectChunkList,
|
||||
} from '@/hooks/use-chunk-request';
|
||||
import { useKnowledgeBaseId } from '@/hooks/use-knowledge-request';
|
||||
import {
|
||||
ArrowLeftOutlined,
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
FilePdfOutlined,
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Input,
|
||||
Menu,
|
||||
MenuProps,
|
||||
Popover,
|
||||
Radio,
|
||||
RadioChangeEvent,
|
||||
Segmented,
|
||||
SegmentedProps,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { ChunkTextMode } from '../../constant';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
interface IProps extends Pick<
|
||||
IChunkListResult,
|
||||
'searchString' | 'handleInputChange' | 'available' | 'handleSetAvailable'
|
||||
> {
|
||||
checked: boolean;
|
||||
selectAllChunk: (checked: boolean) => void;
|
||||
createChunk: () => void;
|
||||
removeChunk: () => void;
|
||||
switchChunk: (available: number) => void;
|
||||
changeChunkTextMode(mode: ChunkTextMode): void;
|
||||
}
|
||||
|
||||
const ChunkToolBar = ({
|
||||
selectAllChunk,
|
||||
checked,
|
||||
createChunk,
|
||||
removeChunk,
|
||||
switchChunk,
|
||||
changeChunkTextMode,
|
||||
available,
|
||||
handleSetAvailable,
|
||||
searchString,
|
||||
handleInputChange,
|
||||
}: IProps) => {
|
||||
const data = useSelectChunkList();
|
||||
const documentInfo = data?.documentInfo;
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
const [isShowSearchBox, setIsShowSearchBox] = useState(false);
|
||||
const { t } = useTranslate('chunk');
|
||||
|
||||
const handleSelectAllCheck = useCallback(
|
||||
(e: any) => {
|
||||
selectAllChunk(e.target.checked);
|
||||
},
|
||||
[selectAllChunk],
|
||||
);
|
||||
|
||||
const handleSearchIconClick = () => {
|
||||
setIsShowSearchBox(true);
|
||||
};
|
||||
|
||||
const handleSearchBlur = () => {
|
||||
if (!searchString?.trim()) {
|
||||
setIsShowSearchBox(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = useCallback(() => {
|
||||
removeChunk();
|
||||
}, [removeChunk]);
|
||||
|
||||
const handleEnabledClick = useCallback(() => {
|
||||
switchChunk(1);
|
||||
}, [switchChunk]);
|
||||
|
||||
const handleDisabledClick = useCallback(() => {
|
||||
switchChunk(0);
|
||||
}, [switchChunk]);
|
||||
|
||||
const items: MenuProps['items'] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<>
|
||||
<Checkbox onChange={handleSelectAllCheck} checked={checked}>
|
||||
<b>{t('selectAll')}</b>
|
||||
</Checkbox>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<Space onClick={handleEnabledClick}>
|
||||
<CheckCircleOutlined />
|
||||
<b>{t('enabledSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: (
|
||||
<Space onClick={handleDisabledClick}>
|
||||
<CloseCircleOutlined />
|
||||
<b>{t('disabledSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: '4',
|
||||
label: (
|
||||
<Space onClick={handleDelete}>
|
||||
<DeleteOutlined />
|
||||
<b>{t('deleteSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [
|
||||
checked,
|
||||
handleSelectAllCheck,
|
||||
handleDelete,
|
||||
handleEnabledClick,
|
||||
handleDisabledClick,
|
||||
t,
|
||||
]);
|
||||
|
||||
const content = (
|
||||
<Menu style={{ width: 200 }} items={items} selectable={false} />
|
||||
);
|
||||
|
||||
const handleFilterChange = (e: RadioChangeEvent) => {
|
||||
selectAllChunk(false);
|
||||
handleSetAvailable(e.target.value);
|
||||
};
|
||||
|
||||
const filterContent = (
|
||||
<Radio.Group onChange={handleFilterChange} value={available}>
|
||||
<Space direction="vertical">
|
||||
<Radio value={undefined}>{t('all')}</Radio>
|
||||
<Radio value={1}>{t('enabled')}</Radio>
|
||||
<Radio value={0}>{t('disabled')}</Radio>
|
||||
</Space>
|
||||
</Radio.Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex justify="space-between" align="center">
|
||||
<Space size={'middle'}>
|
||||
<Link
|
||||
to={`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`}
|
||||
>
|
||||
<ArrowLeftOutlined />
|
||||
</Link>
|
||||
<FilePdfOutlined />
|
||||
<Text ellipsis={{ tooltip: documentInfo?.name }} style={{ width: 150 }}>
|
||||
{documentInfo?.name}
|
||||
</Text>
|
||||
</Space>
|
||||
<Space>
|
||||
<Segmented
|
||||
options={[
|
||||
{ label: t(ChunkTextMode.Full), value: ChunkTextMode.Full },
|
||||
{ label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse },
|
||||
]}
|
||||
onChange={changeChunkTextMode as SegmentedProps['onChange']}
|
||||
/>
|
||||
<Popover content={content} placement="bottom" arrow={false}>
|
||||
<Button>
|
||||
{t('bulk')}
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
</Popover>
|
||||
{isShowSearchBox ? (
|
||||
<Input
|
||||
size="middle"
|
||||
placeholder={t('search')}
|
||||
prefix={<SearchOutlined />}
|
||||
allowClear
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleSearchBlur}
|
||||
value={searchString}
|
||||
/>
|
||||
) : (
|
||||
<Button icon={<SearchOutlined />} onClick={handleSearchIconClick} />
|
||||
)}
|
||||
|
||||
<Popover content={filterContent} placement="bottom" arrow={false}>
|
||||
<Button icon={<FilterIcon />} />
|
||||
</Popover>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
onClick={() => createChunk()}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChunkToolBar;
|
||||
@ -1,74 +0,0 @@
|
||||
import {
|
||||
useNavigateWithFromState,
|
||||
useSecondPathName,
|
||||
useThirdPathName,
|
||||
} from '@/hooks/route-hook';
|
||||
import { useKnowledgeBaseId } from '@/hooks/use-knowledge-request';
|
||||
import { Breadcrumb } from 'antd';
|
||||
import { ItemType } from 'antd/es/breadcrumb/Breadcrumb';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, Outlet } from 'react-router';
|
||||
import Siderbar from './components/knowledge-sidebar';
|
||||
import { KnowledgeDatasetRouteKey, KnowledgeRouteKey } from './constant';
|
||||
import styles from './index.module.less';
|
||||
|
||||
const KnowledgeAdding = () => {
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const activeKey: KnowledgeRouteKey =
|
||||
(useSecondPathName() as KnowledgeRouteKey) || KnowledgeRouteKey.Dataset;
|
||||
|
||||
const datasetActiveKey: KnowledgeDatasetRouteKey =
|
||||
useThirdPathName() as KnowledgeDatasetRouteKey;
|
||||
|
||||
const gotoList = useNavigateWithFromState();
|
||||
|
||||
const breadcrumbItems: ItemType[] = useMemo(() => {
|
||||
const items: ItemType[] = [
|
||||
{
|
||||
title: (
|
||||
<a onClick={() => gotoList('/knowledge')}>
|
||||
{t('header.knowledgeBase')}
|
||||
</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: datasetActiveKey ? (
|
||||
<Link
|
||||
to={`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`}
|
||||
>
|
||||
{t(`knowledgeDetails.${activeKey}`)}
|
||||
</Link>
|
||||
) : (
|
||||
t(`knowledgeDetails.${activeKey}`)
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (datasetActiveKey) {
|
||||
items.push({
|
||||
title: t(`knowledgeDetails.${datasetActiveKey}`),
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}, [activeKey, datasetActiveKey, gotoList, knowledgeBaseId, t]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.container}>
|
||||
<Siderbar></Siderbar>
|
||||
<div className={styles.contentWrapper}>
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
<div className={styles.content}>
|
||||
<Outlet></Outlet>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default KnowledgeAdding;
|
||||
@ -1,223 +0,0 @@
|
||||
import FilterIcon from '@/assets/filter.svg';
|
||||
import { KnowledgeRouteKey } from '@/constants/knowledge';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import {
|
||||
IChunkListResult,
|
||||
useSelectChunkList,
|
||||
} from '@/hooks/use-chunk-request';
|
||||
import { useKnowledgeBaseId } from '@/hooks/use-knowledge-request';
|
||||
import {
|
||||
ArrowLeftOutlined,
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
FilePdfOutlined,
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Flex,
|
||||
Input,
|
||||
Menu,
|
||||
MenuProps,
|
||||
Popover,
|
||||
Radio,
|
||||
RadioChangeEvent,
|
||||
Segmented,
|
||||
SegmentedProps,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { ChunkTextMode } from '../../constant';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
interface IProps extends Pick<
|
||||
IChunkListResult,
|
||||
'searchString' | 'handleInputChange' | 'available' | 'handleSetAvailable'
|
||||
> {
|
||||
checked: boolean;
|
||||
selectAllChunk: (checked: boolean) => void;
|
||||
createChunk: () => void;
|
||||
removeChunk: () => void;
|
||||
switchChunk: (available: number) => void;
|
||||
changeChunkTextMode(mode: ChunkTextMode): void;
|
||||
}
|
||||
|
||||
const ChunkToolBar = ({
|
||||
selectAllChunk,
|
||||
checked,
|
||||
createChunk,
|
||||
removeChunk,
|
||||
switchChunk,
|
||||
changeChunkTextMode,
|
||||
available,
|
||||
handleSetAvailable,
|
||||
searchString,
|
||||
handleInputChange,
|
||||
}: IProps) => {
|
||||
const data = useSelectChunkList();
|
||||
const documentInfo = data?.documentInfo;
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
const [isShowSearchBox, setIsShowSearchBox] = useState(false);
|
||||
const { t } = useTranslate('chunk');
|
||||
|
||||
const handleSelectAllCheck = useCallback(
|
||||
(e: any) => {
|
||||
selectAllChunk(e.target.checked);
|
||||
},
|
||||
[selectAllChunk],
|
||||
);
|
||||
|
||||
const handleSearchIconClick = () => {
|
||||
setIsShowSearchBox(true);
|
||||
};
|
||||
|
||||
const handleSearchBlur = () => {
|
||||
if (!searchString?.trim()) {
|
||||
setIsShowSearchBox(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = useCallback(() => {
|
||||
removeChunk();
|
||||
}, [removeChunk]);
|
||||
|
||||
const handleEnabledClick = useCallback(() => {
|
||||
switchChunk(1);
|
||||
}, [switchChunk]);
|
||||
|
||||
const handleDisabledClick = useCallback(() => {
|
||||
switchChunk(0);
|
||||
}, [switchChunk]);
|
||||
|
||||
const items: MenuProps['items'] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<>
|
||||
<Checkbox onChange={handleSelectAllCheck} checked={checked}>
|
||||
<b>{t('selectAll')}</b>
|
||||
</Checkbox>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<Space onClick={handleEnabledClick}>
|
||||
<CheckCircleOutlined />
|
||||
<b>{t('enabledSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: (
|
||||
<Space onClick={handleDisabledClick}>
|
||||
<CloseCircleOutlined />
|
||||
<b>{t('disabledSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: '4',
|
||||
label: (
|
||||
<Space onClick={handleDelete}>
|
||||
<DeleteOutlined />
|
||||
<b>{t('deleteSelected')}</b>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [
|
||||
checked,
|
||||
handleSelectAllCheck,
|
||||
handleDelete,
|
||||
handleEnabledClick,
|
||||
handleDisabledClick,
|
||||
t,
|
||||
]);
|
||||
|
||||
const content = (
|
||||
<Menu style={{ width: 200 }} items={items} selectable={false} />
|
||||
);
|
||||
|
||||
const handleFilterChange = (e: RadioChangeEvent) => {
|
||||
selectAllChunk(false);
|
||||
handleSetAvailable(e.target.value);
|
||||
};
|
||||
|
||||
const filterContent = (
|
||||
<Radio.Group onChange={handleFilterChange} value={available}>
|
||||
<Space direction="vertical">
|
||||
<Radio value={undefined}>{t('all')}</Radio>
|
||||
<Radio value={1}>{t('enabled')}</Radio>
|
||||
<Radio value={0}>{t('disabled')}</Radio>
|
||||
</Space>
|
||||
</Radio.Group>
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex justify="space-between" align="center">
|
||||
<Space size={'middle'}>
|
||||
<Link
|
||||
to={`/knowledge/${KnowledgeRouteKey.Dataset}?id=${knowledgeBaseId}`}
|
||||
>
|
||||
<ArrowLeftOutlined />
|
||||
</Link>
|
||||
<FilePdfOutlined />
|
||||
<Text ellipsis={{ tooltip: documentInfo?.name }} style={{ width: 150 }}>
|
||||
{documentInfo?.name}
|
||||
</Text>
|
||||
</Space>
|
||||
<Space>
|
||||
<Segmented
|
||||
options={[
|
||||
{ label: t(ChunkTextMode.Full), value: ChunkTextMode.Full },
|
||||
{ label: t(ChunkTextMode.Ellipse), value: ChunkTextMode.Ellipse },
|
||||
]}
|
||||
onChange={changeChunkTextMode as SegmentedProps['onChange']}
|
||||
/>
|
||||
<Popover content={content} placement="bottom" arrow={false}>
|
||||
<Button>
|
||||
{t('bulk')}
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
</Popover>
|
||||
{isShowSearchBox ? (
|
||||
<Input
|
||||
size="middle"
|
||||
placeholder={t('search')}
|
||||
prefix={<SearchOutlined />}
|
||||
allowClear
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleSearchBlur}
|
||||
value={searchString}
|
||||
/>
|
||||
) : (
|
||||
<Button icon={<SearchOutlined />} onClick={handleSearchIconClick} />
|
||||
)}
|
||||
|
||||
<Popover content={filterContent} placement="bottom" arrow={false}>
|
||||
<Button icon={<FilterIcon />} />
|
||||
</Popover>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
onClick={() => createChunk()}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChunkToolBar;
|
||||
Reference in New Issue
Block a user