V4.6.9-first commit (#899)

* perf: insert mongo dataset data session

* perf: dataset data index

* remove delay

* rename bill schema

* rename bill record

* perf: bill table

* perf: prompt

* perf: sub plan

* change the usage count

* feat: usage bill

* publish usages

* doc

* 新增团队聊天功能 (#20)

* perf: doc

* feat 添加标签部分

feat 信息团队标签配置

feat 新增团队同步管理

feat team分享页面

feat 完成team分享页面

feat 实现模糊搜索

style 格式化

fix 修复迷糊匹配

style 样式修改

fix 团队标签功能修复

* fix 修复鉴权功能

* merge 合并代码

* fix 修复引用错误

* fix 修复pr问题

* fix 修复ts格式问题

---------

Co-authored-by: archer <545436317@qq.com>
Co-authored-by: liuxingwan <liuxingwan.lxw@alibaba-inc.com>

* update extra plan

* fix: ts

* format

* perf: bill field

* feat: standard plan

* fix: ts

* feat 个人账号页面修改 (#22)

* feat 添加标签部分

feat 信息团队标签配置

feat 新增团队同步管理

feat team分享页面

feat 完成team分享页面

feat 实现模糊搜索

style 格式化

fix 修复迷糊匹配

style 样式修改

fix 团队标签功能修复

* fix 修复鉴权功能

* merge 合并代码

* fix 修复引用错误

* fix 修复pr问题

* fix 修复ts格式问题

* feat 修改个人账号页

---------

Co-authored-by: liuxingwan <liuxingwan.lxw@alibaba-inc.com>

* sub plan page (#23)

* fix chunk index; error page text

* feat: dataset process Integral prediction

* feat: stand plan field

* feat: sub plan limit

* perf: index

* query extension

* perf: share link push app name

* perf: plan point unit

* perf: get sub plan

* perf: account page

* feat 新增套餐详情弹窗代码 (#24)

* merge 合并代码

* fix 新增套餐详情弹框

* fix 修复pr问题

* feat: change http node input to prompt editor (#21)

* feat: change http node input to prompt editor

* fix

* split PromptEditor to HttpInput

* Team plans (#25)

* perf: pay check

* perf: team plan test

* plan limit check

* replace sensitive text

* perf: fix some null

* collection null check

* perf: plans modal

* perf: http module

* pacakge (#26)

* individuation page and pay modal amount (#27)

* feat: individuation page

* team chat config

* pay modal

* plan count and replace invalid chars (#29)

* fix: user oneapi

* fix: training queue

* fix: qa queue

* perf: remove space chars

* replace invalid chars

* change httpinput dropdown menu (#28)

* perf: http

* reseet free plan

* perf: plan code to packages

* remove llm config to package

* perf: code

* perf: faq

* fix: get team plan

---------

Co-authored-by: yst <77910600+yu-and-liu@users.noreply.github.com>
Co-authored-by: liuxingwan <liuxingwan.lxw@alibaba-inc.com>
Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-02-28 13:19:15 +08:00
committed by GitHub
parent 32686f9e3e
commit 064c64e74c
282 changed files with 7223 additions and 4731 deletions

View File

@@ -27,6 +27,8 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import Tabs from '@/components/Tabs';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
import SelectAiModel from '@/components/Select/SelectAiModel';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useToast } from '@fastgpt/web/hooks/useToast';
export type DatasetParamsProps = {
searchMode: `${DatasetSearchModeEnum}`;
@@ -61,6 +63,8 @@ const DatasetParamsModal = ({
}: DatasetParamsProps & { onClose: () => void; onSuccess: (e: DatasetParamsProps) => void }) => {
const { t } = useTranslation();
const theme = useTheme();
const { toast } = useToast();
const { teamPlanStatus } = useUserStore();
const { reRankModelList, llmModelList } = useSystemStore();
const [refresh, setRefresh] = useState(false);
const [currentTabType, setCurrentTabType] = useState(SearchSettingTabEnum.searchMode);
@@ -71,7 +75,7 @@ const DatasetParamsModal = ({
limit,
similarity,
searchMode,
usingReRank,
usingReRank: !!usingReRank && !!teamPlanStatus?.standardConstants?.permissionReRank,
datasetSearchUsingExtensionQuery,
datasetSearchExtensionModel: datasetSearchExtensionModel ?? llmModelList[0]?.model,
datasetSearchExtensionBg
@@ -105,6 +109,10 @@ const DatasetParamsModal = ({
return true;
}, [getValues, similarity]);
const showReRank = useMemo(() => {
return usingReRank !== undefined && reRankModelList.length > 0;
}, [reRankModelList.length, usingReRank]);
return (
<MyModal
isOpen={true}
@@ -148,7 +156,7 @@ const DatasetParamsModal = ({
setRefresh(!refresh);
}}
/>
{usingReRank !== undefined && reRankModelList.length > 0 && (
{showReRank && (
<>
<Divider my={4} />
<Flex
@@ -168,6 +176,15 @@ const DatasetParamsModal = ({
}
: {})}
onClick={(e) => {
if (
teamPlanStatus?.standardConstants &&
!teamPlanStatus?.standardConstants?.permissionReRank
) {
return toast({
status: 'warning',
title: t('support.team.limit.No permission rerank')
});
}
setValue('usingReRank', !getValues('usingReRank'));
setRefresh((state) => !state);
}}
@@ -273,7 +290,7 @@ const DatasetParamsModal = ({
{currentTabType === SearchSettingTabEnum.queryExtension && (
<Box>
<Box fontSize={'xs'} color={'myGray.500'}>
{t('core.module.template.Query extension intro')}
{t('core.dataset.Query extension intro')}
</Box>
<Flex mt={3} alignItems={'center'}>
<Box flex={'1 0 0'}>{t('core.dataset.search.Using query extension')}</Box>

View File

@@ -0,0 +1,185 @@
import React from 'react';
import MyModal from '@/components/MyModal';
import { ModalBody, Button, ModalFooter, useDisclosure, Textarea, Box } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { onChangeNode } from '../../../FlowProvider';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';
import { useToast } from '@fastgpt/web/hooks/useToast';
import yaml from 'js-yaml';
import { useForm } from 'react-hook-form';
type RequestMethod = 'get' | 'post' | 'put' | 'delete' | 'patch';
const methodMap: { [K in RequestMethod]: string } = {
get: 'GET',
post: 'POST',
put: 'PUT',
delete: 'DELETE',
patch: 'PATCH'
};
const OpenApiImportModal = ({
children,
moduleId,
inputs
}: {
children: React.ReactElement;
moduleId: string;
inputs: FlowNodeInputItemType[];
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const { t } = useTranslation();
const { register, handleSubmit } = useForm({
defaultValues: {
openapiContent: ''
}
});
const { toast } = useToast();
const handleFileProcessing = async (content: string) => {
try {
let data;
try {
data = JSON.parse(content);
} catch (jsonError) {
try {
data = yaml.load(content, { schema: yaml.FAILSAFE_SCHEMA });
} catch (yamlError) {
console.error(yamlError);
throw new Error();
}
}
const firstPathName = Object.keys(data.paths)[0];
const firstPathData = data.paths[firstPathName];
const firstRequestMethod = Object.keys(firstPathData)[0];
const firstRequestMethodData = firstPathData[firstRequestMethod];
const firstRequestParameters = firstRequestMethodData.parameters || [];
const pathParams = [];
const headerParams = [];
for (const parameter of firstRequestParameters) {
if (parameter.in === 'path') {
pathParams.push({
key: parameter.name,
type: parameter.schema.type
});
} else {
headerParams.push({
key: parameter.name,
type: parameter.schema.type
});
}
}
const requestBodySchema =
firstRequestMethodData.requestBody?.content?.['application/json']?.schema;
let requestBodyValue = '';
if (requestBodySchema) {
requestBodyValue = JSON.stringify(requestBodySchema, null, 2);
}
const requestUrl = inputs.find((item) => item.key === ModuleInputKeyEnum.httpReqUrl);
const requestMethod = inputs.find((item) => item.key === ModuleInputKeyEnum.httpMethod);
const params = inputs.find((item) => item.key === ModuleInputKeyEnum.httpParams);
const headers = inputs.find((item) => item.key === ModuleInputKeyEnum.httpHeaders);
const jsonBody = inputs.find((item) => item.key === ModuleInputKeyEnum.httpJsonBody);
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.httpReqUrl,
value: {
...requestUrl,
value: firstPathName
}
});
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.httpMethod,
value: {
...requestMethod,
value: methodMap[firstRequestMethod.toLowerCase() as RequestMethod] || 'GET'
}
});
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.httpParams,
value: {
...params,
value: pathParams
}
});
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.httpHeaders,
value: {
...headers,
value: headerParams
}
});
onChangeNode({
moduleId,
type: 'updateInput',
key: ModuleInputKeyEnum.httpJsonBody,
value: {
...jsonBody,
value: requestBodyValue
}
});
onClose();
toast({
title: t('common.Import success'),
status: 'success'
});
} catch (error: any) {
toast({
title: t('common.Import failed'),
description: error.message,
status: 'error'
});
console.error(error);
}
};
return (
<>
{children && <Box onClick={onOpen}>{children}</Box>}
<MyModal
isOpen={isOpen}
onClose={onClose}
iconSrc="modal/edit"
title={t('common.Import')}
m={'auto'}
w={500}
>
<ModalBody>
<Textarea
height={400}
maxH={500}
mt={2}
{...register('openapiContent')}
placeholder={t('core.module.http.OpenAPI import placeholder')}
/>
</ModalBody>
<ModalFooter>
<Button onClick={handleSubmit((data) => handleFileProcessing(data.openapiContent))}>
{t('common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
</>
);
};
export default React.memo(OpenApiImportModal);

View File

@@ -1,11 +1,11 @@
import React, { useCallback, useMemo, useState, useTransition } from 'react';
import React, { useCallback, useEffect, useMemo, useState, useTransition } from 'react';
import { NodeProps } from 'reactflow';
import NodeCard from '../render/NodeCard';
import NodeCard from '../../render/NodeCard';
import { FlowModuleItemType } from '@fastgpt/global/core/module/type.d';
import Divider from '../modules/Divider';
import Container from '../modules/Container';
import RenderInput from '../render/RenderInput';
import RenderOutput from '../render/RenderOutput';
import Divider from '../../modules/Divider';
import Container from '../../modules/Container';
import RenderInput from '../../render/RenderInput';
import RenderOutput from '../../render/RenderOutput';
import {
Box,
Flex,
@@ -16,17 +16,17 @@ import {
Tr,
Th,
Td,
TableContainer
TableContainer,
Button
} from '@chakra-ui/react';
import MySelect from '@/components/Select';
import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
import { onChangeNode, useFlowProviderStore } from '../../FlowProvider';
import { onChangeNode, useFlowProviderStore } from '../../../FlowProvider';
import { useTranslation } from 'next-i18next';
import Tabs from '@/components/Tabs';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useForm } from 'react-hook-form';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import JSONEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
@@ -36,6 +36,9 @@ import {
splitGuideModule
} from '@fastgpt/global/core/module/utils';
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
import HttpInput from '@fastgpt/web/components/common/Input/HttpInput';
import dynamic from 'next/dynamic';
const OpenApiImportModal = dynamic(() => import('./OpenApiImportModal'));
enum TabEnum {
params = 'params',
@@ -133,11 +136,18 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
return (
<Box>
<Box mb={2}>{t('core.module.Http request settings')}</Box>
<Box mb={2} display={'flex'} justifyContent={'space-between'}>
<span>{t('core.module.Http request settings')}</span>
<span>
<OpenApiImportModal moduleId={moduleId} inputs={inputs}>
<Button variant={'link'}>{t('core.module.http.OpenAPI import')}</Button>
</OpenApiImportModal>
</span>
</Box>
<Flex alignItems={'center'} className="nodrag">
<MySelect
h={'34px'}
w={'80px'}
w={'88px'}
bg={'myGray.50'}
width={'100%'}
value={requestMethods?.value}
@@ -149,6 +159,18 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
{
label: 'POST',
value: 'POST'
},
{
label: 'PUT',
value: 'PUT'
},
{
label: 'DELETE',
value: 'DELETE'
},
{
label: 'PATCH',
value: 'PATCH'
}
]}
onchange={(e) => {
@@ -178,10 +200,6 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
);
});
const defaultForm = {
key: '',
value: ''
};
function RenderHttpProps({
moduleId,
inputs
@@ -261,7 +279,7 @@ function RenderHttpProps({
<Tabs
list={[
{ label: <RenderPropsItem text="Params" num={paramsLength} />, id: TabEnum.params },
...(requestMethods === 'POST'
...(!['GET', 'DELETE'].includes(requestMethods)
? [
{
label: (
@@ -303,36 +321,75 @@ const RenderForm = ({
}) => {
const { t } = useTranslation();
const { toast } = useToast();
const [_, startSts] = useTransition();
const { register, reset, handleSubmit } = useForm({
defaultValues: defaultForm
});
const list = useMemo(() => (input.value || []) as PropsArrType[], [input.value]);
const [list, setList] = useState<PropsArrType[]>(input.value || []);
const [updateTrigger, setUpdateTrigger] = useState(false);
const [shouldUpdateNode, setShouldUpdateNode] = useState(false);
const addNewProps = useCallback(
({ key, value }: { key: string; value: string }) => {
const checkExist = list.find((item) => item.key === key);
if (checkExist) {
return toast({
status: 'warning',
title: t('core.module.http.Key already exists')
});
}
if (!key) return;
const leftVariables = useMemo(() => {
return variables.filter((variable) => {
const existVariables = list.map((item) => item.key);
return !existVariables.includes(variable.key);
});
}, [list, variables]);
useEffect(() => {
setList(input.value || []);
}, [input.value]);
useEffect(() => {
if (shouldUpdateNode) {
onChangeNode({
moduleId,
type: 'updateInput',
key: input.key,
value: {
...input,
value: [...list, { key, type: 'string', value }]
value: list
}
});
reset(defaultForm);
},
[input, list, moduleId, reset, t, toast]
);
setShouldUpdateNode(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [list]);
const handleKeyChange = (index: number, newKey: string) => {
setList((prevList) => {
if (!newKey) {
setUpdateTrigger((prev) => !prev);
toast({
status: 'warning',
title: t('core.module.http.Key cannot be empty')
});
return prevList;
}
const checkExist = prevList.find((item, i) => i !== index && item.key == newKey);
if (checkExist) {
setUpdateTrigger((prev) => !prev);
toast({
status: 'warning',
title: t('core.module.http.Key already exists')
});
return prevList;
}
return prevList.map((item, i) => (i === index ? { ...item, key: newKey } : item));
});
setShouldUpdateNode(true);
};
const handleAddNewProps = (key: string, value: string = '') => {
const checkExist = list.find((item) => item.key === key);
if (checkExist) {
return toast({
status: 'warning',
title: t('core.module.http.Key already exists')
});
}
if (!key) return;
setList((prevList) => [...prevList, { key, type: 'string', value }]);
setShouldUpdateNode(true);
};
return (
<TableContainer>
@@ -347,103 +404,69 @@ const RenderForm = ({
{list.map((item, index) => (
<Tr key={`${input.key}${index}`}>
<Td p={0} w={'150px'}>
<Input
w={'150px'}
defaultValue={item.key}
variant={'unstyled'}
paddingLeft={2}
placeholder={t('core.module.http.Props name')}
onBlur={(e) => {
const val = e.target.value;
if (!val) {
return toast({
status: 'warning',
title: t('core.module.http.Key cannot be empty')
});
}
const checkExist = list.find((item, i) => i !== index && item.key == val);
if (checkExist) {
return toast({
status: 'warning',
title: t('core.module.http.Key already exists')
});
}
startSts(() => {
onChangeNode({
moduleId,
type: 'updateInput',
key: input.key,
value: {
...input,
value: list.map((item, i) => (i === index ? { ...item, key: val } : item))
}
});
});
<HttpInput
hasVariablePlugin={false}
hasDropDownPlugin={true}
setDropdownValue={(value) => {
handleKeyChange(index, value);
setUpdateTrigger((prev) => !prev);
}}
placeholder={t('core.module.http.Props name')}
value={item.key}
variables={leftVariables}
onBlur={(val) => {
handleKeyChange(index, val);
}}
updateTrigger={updateTrigger}
/>
</Td>
<Td p={0} display={'flex'} alignItems={'center'}>
<Input
flex={'1 0 0'}
w={'150px'}
defaultValue={item.value}
variant={'unstyled'}
paddingLeft={2}
placeholder={t('core.module.http.Props value')}
onBlur={(e) => {
const val = e.target.value;
startSts(() => {
onChangeNode({
moduleId,
type: 'updateInput',
key: input.key,
value: {
...input,
value: list.map((item, i) =>
i === index ? { ...item, value: val } : item
)
}
});
});
}}
/>
<MyIcon
name={'delete'}
cursor={'pointer'}
_hover={{ color: 'red.600' }}
w={'14px'}
onClick={(e) => {
e.stopPropagation();
onChangeNode({
moduleId,
type: 'updateInput',
key: input.key,
value: {
...input,
value: list.filter((val) => val.key !== item.key)
}
});
}}
/>
<Td p={0}>
<Box display={'flex'} alignItems={'center'}>
<HttpInput
placeholder={t('core.module.http.Props value')}
value={item.value}
variables={variables}
onBlur={(val) => {
setList((prevList) =>
prevList.map((item, i) => (i === index ? { ...item, value: val } : item))
);
setShouldUpdateNode(true);
}}
/>
<MyIcon
name={'delete'}
cursor={'pointer'}
_hover={{ color: 'red.600' }}
w={'14px'}
onClick={() => {
setList((prevlist) => prevlist.filter((val) => val.key !== item.key));
setShouldUpdateNode(true);
}}
/>
</Box>
</Td>
</Tr>
))}
<Tr>
<Td p={0} w={'150px'}>
<Input
w={'150px'}
variant={'unstyled'}
paddingLeft={2}
<HttpInput
hasDropDownPlugin={true}
setDropdownValue={(val) => {
handleAddNewProps(val);
}}
placeholder={t('core.module.http.Add props')}
{...register('key', {
onBlur: handleSubmit(addNewProps)
})}
value={''}
h={40}
variables={leftVariables}
onBlur={(val) => {
handleAddNewProps(val);
}}
/>
</Td>
<Td p={0}>
<Input variant={'unstyled'} paddingLeft={2} {...register('value')} />
<Box display={'flex'} alignItems={'center'}>
<HttpInput />
</Box>
</Td>
</Tr>
</Tbody>
@@ -460,7 +483,9 @@ const RenderJson = ({
input: FlowNodeInputItemType;
variables: EditorVariablePickerType[];
}) => {
const { t } = useTranslation();
const [_, startSts] = useTransition();
return (
<Box mt={1}>
<JSONEditor
@@ -468,6 +493,7 @@ const RenderJson = ({
height={200}
resize
value={input.value}
placeholder={t('core.module.template.http body placeholder')}
onChange={(e) => {
startSts(() => {
onChangeNode({
@@ -488,7 +514,7 @@ const RenderJson = ({
};
const RenderPropsItem = ({ text, num }: { text: string; num: number }) => {
return (
<Flex alignItems={'center'} fontSize={'xs'} transform={'scale(0.8)'}>
<Flex alignItems={'center'}>
<Box>{text}</Box>
{num > 0 && (
<Box ml={1} borderRadius={'50%'} bg={'myGray.200'} px={2} py={'1px'}>
@@ -500,6 +526,7 @@ const RenderPropsItem = ({ text, num }: { text: string; num: number }) => {
};
const NodeHttp = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
const { t } = useTranslation();
const { moduleId, inputs, outputs } = data;
const CustomComponents = useMemo(
@@ -511,12 +538,12 @@ const NodeHttp = ({ data, selected }: NodeProps<FlowModuleItemType>) => {
<>
<RenderHttpProps moduleId={moduleId} inputs={inputs} />
<Box mt={2} transform={'translateY(10px)'}>
{t('core.module.Variable import')}
</Box>
</>
)
}),
[inputs, moduleId]
[inputs, moduleId, t]
);
return (

View File

@@ -93,47 +93,38 @@ const FieldEditModal = ({
const { register, getValues, setValue, handleSubmit, watch } = useForm<EditNodeFieldType>({
defaultValues: defaultField
});
const inputType = watch('inputType');
const outputType = watch('outputType');
const valueType = watch('valueType');
const [refresh, setRefresh] = useState(false);
const showDataTypeSelect = useMemo(() => {
if (!editField.dataType) return false;
const inputType = getValues('inputType');
const outputType = getValues('outputType');
if (inputType === FlowNodeInputTypeEnum.target) return true;
if (outputType === FlowNodeOutputTypeEnum.source) return true;
return false;
}, [editField.dataType, getValues, refresh]);
}, [editField.dataType, inputType, outputType]);
const showRequired = useMemo(() => {
const inputType = getValues('inputType');
const valueType = getValues('valueType');
if (inputType === FlowNodeInputTypeEnum.addInputParam) return false;
return editField.required;
}, [editField.required, getValues, refresh]);
}, [editField.required, inputType]);
const showNameInput = useMemo(() => {
const inputType = getValues('inputType');
return editField.name;
}, [editField.name, getValues, refresh]);
}, [editField.name]);
const showKeyInput = useMemo(() => {
const inputType = getValues('inputType');
const valueType = getValues('valueType');
if (inputType === FlowNodeInputTypeEnum.addInputParam) return false;
return editField.key;
}, [editField.key, getValues, refresh]);
}, [editField.key, inputType]);
const showDescriptionInput = useMemo(() => {
const inputType = getValues('inputType');
return editField.description;
}, [editField.description, getValues, refresh]);
}, [editField.description]);
return (
<MyModal
@@ -209,7 +200,18 @@ const FieldEditModal = ({
{showKeyInput && (
<Flex mb={5} alignItems={'center'}>
<Box flex={'0 0 70px'}>{t('core.module.Field key')}</Box>
<Input placeholder="appointment/sql" {...register('key', { required: true })} />
<Input
placeholder="appointment/sql"
{...register('key', {
required: true,
onChange: (e) => {
const value = e.target.value;
if (!showNameInput) {
setValue('label', value);
}
}
})}
/>
</Flex>
)}
{showDescriptionInput && (

View File

@@ -46,11 +46,7 @@ const RenderList: {
Component: dynamic(() => import('./templates/AiSetting'))
},
{
types: [
FlowNodeInputTypeEnum.selectChatModel,
FlowNodeInputTypeEnum.selectCQModel,
FlowNodeInputTypeEnum.selectExtractModel
],
types: [FlowNodeInputTypeEnum.selectLLMModel],
Component: dynamic(() => import('./templates/SelectAiModel'))
},
{

View File

@@ -55,7 +55,7 @@ const JsonEditor = ({ inputs = [], item, moduleId }: RenderInputProps) => {
return (
<JSONEditor
bg={'myGray.50'}
placeholder={t(item.placeholder || '')}
placeholder={item.placeholder}
resize
value={value}
onChange={(e) => {

View File

@@ -33,7 +33,7 @@ const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
[FlowNodeTypeEnum.pluginInput]: dynamic(() => import('./components/nodes/NodePluginInput')),
[FlowNodeTypeEnum.pluginOutput]: dynamic(() => import('./components/nodes/NodePluginOutput')),
[FlowNodeTypeEnum.pluginModule]: NodeSimple,
[FlowNodeTypeEnum.cfr]: NodeSimple
[FlowNodeTypeEnum.queryExtension]: NodeSimple
};
const edgeTypes = {
[EDGE_TYPE]: ButtonEdge