feat: modify the style of the operator #918 (#1335)

### What problem does this PR solve?

feat: modify the style of the operator #918
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-07-01 18:58:51 +08:00 committed by GitHub
parent b5389f487c
commit 5fa3c2bdce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 134 additions and 74 deletions

View File

@ -1,3 +1,4 @@
.delete { .delete {
height: 24px; // height: 24px;
display: inline-block;
} }

View File

@ -10,6 +10,7 @@ interface IProps {
deleteItem: () => Promise<any> | void; deleteItem: () => Promise<any> | void;
iconFontSize?: number; iconFontSize?: number;
items?: MenuProps['items']; items?: MenuProps['items'];
height?: number;
} }
const OperateDropdown = ({ const OperateDropdown = ({
@ -17,6 +18,7 @@ const OperateDropdown = ({
children, children,
iconFontSize = 30, iconFontSize = 30,
items: otherItems = [], items: otherItems = [],
height = 24,
}: React.PropsWithChildren<IProps>) => { }: React.PropsWithChildren<IProps>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
@ -59,7 +61,12 @@ const OperateDropdown = ({
<span className={styles.delete}> <span className={styles.delete}>
<MoreOutlined <MoreOutlined
rotate={90} rotate={90}
style={{ fontSize: iconFontSize, color: 'gray', cursor: 'pointer' }} style={{
fontSize: iconFontSize,
color: 'gray',
cursor: 'pointer',
height,
}}
/> />
</span> </span>
)} )}

View File

@ -1,10 +1,8 @@
import { Flex, Space } from 'antd'; import { Flex } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow'; import { Handle, NodeProps, Position } from 'reactflow';
import { Operator } from '../../constant'; import { Operator, operatorMap } from '../../constant';
import { NodeData } from '../../interface'; import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import NodeDropdown from './dropdown';
import styles from './index.less'; import styles from './index.less';
@ -15,6 +13,12 @@ export function BeginNode({ id, data, selected }: NodeProps<NodeData>) {
className={classNames(styles.ragNode, { className={classNames(styles.ragNode, {
[styles.selectedNode]: selected, [styles.selectedNode]: selected,
})} })}
style={{
backgroundColor: operatorMap[data.label as Operator].backgroundColor,
color: 'white',
width: 50,
height: 50,
}}
> >
<Handle <Handle
type="source" type="source"
@ -22,14 +26,8 @@ export function BeginNode({ id, data, selected }: NodeProps<NodeData>) {
isConnectable isConnectable
className={styles.handle} className={styles.handle}
></Handle> ></Handle>
<Flex vertical align="center" justify="center"> <Flex vertical align="center" justify="center" gap={6}>
<Space size={6}> <span className={styles.type}>{data.label}</span>
<OperatorIcon
name={data.label as Operator}
fontSize={16}
></OperatorIcon>
<NodeDropdown id={id}></NodeDropdown>
</Space>
</Flex> </Flex>
<section className={styles.bottomBox}> <section className={styles.bottomBox}>
<div className={styles.nodeName}>{data.name}</div> <div className={styles.nodeName}>{data.name}</div>

View File

@ -1,8 +1,12 @@
import { Flex, Space } from 'antd'; import { Flex } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import get from 'lodash/get'; import get from 'lodash/get';
import { Handle, NodeProps, Position } from 'reactflow'; import { Handle, NodeProps, Position } from 'reactflow';
import { CategorizeAnchorPointPositions, Operator } from '../../constant'; import {
CategorizeAnchorPointPositions,
Operator,
operatorMap,
} from '../../constant';
import { NodeData } from '../../interface'; import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon'; import OperatorIcon from '../../operator-icon';
import CategorizeHandle from './categorize-handle'; import CategorizeHandle from './categorize-handle';
@ -12,12 +16,17 @@ import styles from './index.less';
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) { export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
const categoryData = get(data, 'form.category_description') ?? {}; const categoryData = get(data, 'form.category_description') ?? {};
const style = operatorMap[data.label as Operator];
return ( return (
<section <section
className={classNames(styles.ragNode, { className={classNames(styles.ragNode, {
[styles.selectedNode]: selected, [styles.selectedNode]: selected,
})} })}
style={{
backgroundColor: style.backgroundColor,
color: style.color,
}}
> >
<Handle <Handle
type="target" type="target"
@ -49,14 +58,13 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
idx={idx} idx={idx}
></CategorizeHandle> ></CategorizeHandle>
))} ))}
<Flex vertical align="center" justify="center"> <Flex vertical align="center" justify="center" gap={6}>
<Space size={6}> <OperatorIcon
<OperatorIcon name={data.label as Operator}
name={data.label as Operator} fontSize={24}
fontSize={16} ></OperatorIcon>
></OperatorIcon> <span className={styles.type}>{data.label}</span>
<NodeDropdown id={id}></NodeDropdown> <NodeDropdown id={id}></NodeDropdown>
</Space>
</Flex> </Flex>
<section className={styles.bottomBox}> <section className={styles.bottomBox}>
<div className={styles.nodeName}>{data.name}</div> <div className={styles.nodeName}>{data.name}</div>

View File

@ -38,6 +38,7 @@ const NodeDropdown = ({ id }: IProps) => {
return ( return (
<OperateDropdown <OperateDropdown
iconFontSize={14} iconFontSize={14}
height={14}
deleteItem={deleteNode} deleteItem={deleteNode}
items={items} items={items}
></OperateDropdown> ></OperateDropdown>

View File

@ -23,6 +23,9 @@
color: #777; color: #777;
font-size: 12px; font-size: 12px;
} }
.type {
// font-size: 12px;
}
.description { .description {
font-size: 10px; font-size: 10px;
} }

View File

@ -1,9 +1,13 @@
import { Flex } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
import { Flex, Space } from 'antd';
import get from 'lodash/get'; import get from 'lodash/get';
import { CategorizeAnchorPointPositions, Operator } from '../../constant'; import pick from 'lodash/pick';
import { Handle, NodeProps, Position } from 'reactflow';
import {
CategorizeAnchorPointPositions,
Operator,
operatorMap,
} from '../../constant';
import { NodeData } from '../../interface'; import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon'; import OperatorIcon from '../../operator-icon';
import CategorizeHandle from './categorize-handle'; import CategorizeHandle from './categorize-handle';
@ -18,12 +22,14 @@ export function RagNode({
}: NodeProps<NodeData>) { }: NodeProps<NodeData>) {
const isCategorize = data.label === Operator.Categorize; const isCategorize = data.label === Operator.Categorize;
const categoryData = get(data, 'form.category_description') ?? {}; const categoryData = get(data, 'form.category_description') ?? {};
const style = operatorMap[data.label as Operator];
return ( return (
<section <section
className={classNames(styles.ragNode, { className={classNames(styles.ragNode, {
[styles.selectedNode]: selected, [styles.selectedNode]: selected,
})} })}
style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
> >
<Handle <Handle
id="c" id="c"
@ -51,14 +57,18 @@ export function RagNode({
idx={idx} idx={idx}
></CategorizeHandle> ></CategorizeHandle>
))} ))}
<Flex vertical align="center" justify="center"> <Flex vertical align="center" justify={'center'} gap={6}>
<Space size={6}> <OperatorIcon
<OperatorIcon name={data.label as Operator}
name={data.label as Operator} fontSize={style['iconFontSize'] ?? 24}
fontSize={16} ></OperatorIcon>
></OperatorIcon> <span
<NodeDropdown id={id}></NodeDropdown> className={styles.type}
</Space> style={{ fontSize: style.fontSize ?? 14 }}
>
{data.label}
</span>
<NodeDropdown id={id}></NodeDropdown>
</Flex> </Flex>
<section className={styles.bottomBox}> <section className={styles.bottomBox}>

View File

@ -34,14 +34,45 @@ export const operatorIconMap = {
export const operatorMap = { export const operatorMap = {
[Operator.Retrieval]: { [Operator.Retrieval]: {
description: 'Retrieval description drjlftglrthjftl', description: 'Retrieval description drjlftglrthjftl',
backgroundColor: '#cad6e0',
color: '#385974',
},
[Operator.Generate]: {
description: 'Generate description',
backgroundColor: '#ebd6d6',
width: 150,
height: 150,
fontSize: 20,
iconFontSize: 30,
color: '#996464',
},
[Operator.Answer]: {
description: 'Answer description',
backgroundColor: '#f4816d',
color: 'white',
},
[Operator.Begin]: {
description: 'Begin description',
backgroundColor: '#4f51d6',
},
[Operator.Categorize]: {
description: 'Categorize description',
backgroundColor: '#ffebcd',
color: '#cc8a26',
},
[Operator.Message]: {
description: 'Message description',
backgroundColor: '#c5ddc7',
color: 'green',
},
[Operator.Relevant]: {
description: 'BranchesOutlined description',
backgroundColor: 'white',
},
[Operator.RewriteQuestion]: {
description: 'RewriteQuestion description',
backgroundColor: 'white',
}, },
[Operator.Generate]: { description: 'Generate description' },
[Operator.Answer]: { description: 'Answer description' },
[Operator.Begin]: { description: 'Begin description' },
[Operator.Categorize]: { description: 'Categorize description' },
[Operator.Message]: { description: 'Message description' },
[Operator.Relevant]: { description: 'BranchesOutlined description' },
[Operator.RewriteQuestion]: { description: 'RewriteQuestion description' },
}; };
export const componentMenuList = [ export const componentMenuList = [

View File

@ -1,7 +1,7 @@
{ {
"edges": [ "edges": [
{ {
"id": "1542fe3d-d13d-4e14-a253-c06cdf72e357", "id": "01dc9019-5c4c-4410-9759-f09ad8075a23",
"label": "", "label": "",
"source": "begin", "source": "begin",
"target": "answer:0", "target": "answer:0",
@ -10,7 +10,7 @@
} }
}, },
{ {
"id": "e0c46945-b60a-4da9-9a35-5dd654469e47", "id": "f0216178-c3ab-48c1-8020-c5c7d8aa3853",
"label": "", "label": "",
"source": "message:reject", "source": "message:reject",
"target": "answer:0", "target": "answer:0",
@ -19,7 +19,7 @@
} }
}, },
{ {
"id": "5806636c-2bba-4c14-922e-3ef905c37f52", "id": "fd98adb0-9461-45ba-9f6e-5633b8ea57ac",
"label": "", "label": "",
"source": "answer:0", "source": "answer:0",
"target": "categorize:0", "target": "categorize:0",
@ -28,7 +28,7 @@
} }
}, },
{ {
"id": "ebce1598-cd0c-4863-a8b1-2f8a1de28040", "id": "c4ea0be6-d98c-464c-89c4-84b4199e6128",
"label": "", "label": "",
"source": "categorize:0", "source": "categorize:0",
"target": "message:introduction", "target": "message:introduction",
@ -38,7 +38,7 @@
"sourceHandle": "interested" "sourceHandle": "interested"
}, },
{ {
"id": "1e560fed-76f9-494b-a028-cd871acdee07", "id": "145f4531-c5e4-4e01-9378-128b84db6029",
"label": "", "label": "",
"source": "categorize:0", "source": "categorize:0",
"target": "generate:casual", "target": "generate:casual",
@ -48,7 +48,7 @@
"sourceHandle": "casual" "sourceHandle": "casual"
}, },
{ {
"id": "5aa430cc-19c4-4f82-9fed-5649651fff11", "id": "1b8f7b90-4ee8-471a-aea0-4ae1fba7092b",
"label": "", "label": "",
"source": "categorize:0", "source": "categorize:0",
"target": "message:reject", "target": "message:reject",
@ -58,7 +58,7 @@
"sourceHandle": "answer" "sourceHandle": "answer"
}, },
{ {
"id": "c40b1dab-5f42-425f-9207-27e261d6b70f", "id": "1e9c2335-a293-492f-a3c9-6f9bd02b7f91",
"label": "", "label": "",
"source": "categorize:0", "source": "categorize:0",
"target": "retrieval:0", "target": "retrieval:0",
@ -68,7 +68,7 @@
"sourceHandle": "about_job" "sourceHandle": "about_job"
}, },
{ {
"id": "7216138f-cdc0-4992-851e-30916033d520", "id": "c2b63dca-974f-4d48-8685-269230ff41ad",
"label": "", "label": "",
"source": "message:introduction", "source": "message:introduction",
"target": "answer:1", "target": "answer:1",
@ -77,7 +77,7 @@
} }
}, },
{ {
"id": "3bb8ada2-b1ac-49cc-81bf-78ffb2c07d94", "id": "1e7a0f1f-3377-4762-a15d-975d8ac3b67a",
"label": "", "label": "",
"source": "generate:aboutJob", "source": "generate:aboutJob",
"target": "answer:1", "target": "answer:1",
@ -86,7 +86,7 @@
} }
}, },
{ {
"id": "d60c4c33-ddd2-40ff-af62-aabb11e6a91c", "id": "0d860594-9339-4283-9cc0-228f6f3e15a1",
"label": "", "label": "",
"source": "generate:casual", "source": "generate:casual",
"target": "answer:1", "target": "answer:1",
@ -95,7 +95,7 @@
} }
}, },
{ {
"id": "f425b2ec-fe5b-44d9-b5f3-c2953e12b600", "id": "0707e61d-95ed-4c30-95ce-83ae930f3079",
"label": "", "label": "",
"source": "generate:get_wechat", "source": "generate:get_wechat",
"target": "answer:1", "target": "answer:1",
@ -104,7 +104,7 @@
} }
}, },
{ {
"id": "ebf55d7f-36bf-43ba-9166-34d8a4a68474", "id": "a0e1cb13-8667-47ba-8b8a-11974f68b628",
"label": "", "label": "",
"source": "generate:nowechat", "source": "generate:nowechat",
"target": "answer:1", "target": "answer:1",
@ -113,7 +113,7 @@
} }
}, },
{ {
"id": "8897e8ed-12b7-4ca4-a63b-7a5a702a1517", "id": "071f6b90-154a-421d-a3a1-24d68120517b",
"label": "", "label": "",
"source": "answer:1", "source": "answer:1",
"target": "categorize:1", "target": "categorize:1",
@ -122,7 +122,7 @@
} }
}, },
{ {
"id": "a0d646e9-6ef9-490d-9308-830c51b8a663", "id": "d0daf286-afe3-4257-ad23-62280de6a3c9",
"label": "", "label": "",
"source": "categorize:1", "source": "categorize:1",
"target": "retrieval:0", "target": "retrieval:0",
@ -132,7 +132,7 @@
"sourceHandle": "about_job" "sourceHandle": "about_job"
}, },
{ {
"id": "6a0714f7-806f-49f4-bc36-210025e48f49", "id": "eed037f7-7a2c-4863-8daa-95299c0d7ed0",
"label": "", "label": "",
"source": "categorize:1", "source": "categorize:1",
"target": "generate:casual", "target": "generate:casual",
@ -142,7 +142,7 @@
"sourceHandle": "casual" "sourceHandle": "casual"
}, },
{ {
"id": "9267def3-9b81-4f50-87da-0eef85b4fe90", "id": "e2ce8e2c-5383-4328-8c7f-6a1ea5fbe392",
"label": "", "label": "",
"source": "categorize:1", "source": "categorize:1",
"target": "generate:get_wechat", "target": "generate:get_wechat",
@ -152,7 +152,7 @@
"sourceHandle": "wechat" "sourceHandle": "wechat"
}, },
{ {
"id": "d8bfe795-36e7-4172-9dfe-649ad37be4d8", "id": "b4e00364-bf13-4a5c-ba15-c10080a01faa",
"label": "", "label": "",
"source": "categorize:1", "source": "categorize:1",
"target": "generate:nowechat", "target": "generate:nowechat",
@ -162,7 +162,7 @@
"sourceHandle": "giveup" "sourceHandle": "giveup"
}, },
{ {
"id": "94c5e5c5-3b35-412d-a6cb-4ed5f883fef2", "id": "d562f6f1-e9e3-4bf4-bcf1-171e3bdd8bca",
"label": "", "label": "",
"source": "retrieval:0", "source": "retrieval:0",
"target": "generate:aboutJob", "target": "generate:aboutJob",
@ -171,7 +171,7 @@
} }
}, },
{ {
"id": "17da25cc-09c5-4a5e-ad27-2c6593f8fff5", "id": "063838ab-7813-4981-b633-34f6d485305b",
"label": "", "label": "",
"source": "relevant:0", "source": "relevant:0",
"target": "generate:aboutJob", "target": "generate:aboutJob",
@ -190,7 +190,7 @@
}, },
"data": { "data": {
"label": "Begin", "label": "Begin",
"name": "OliveGuestsPlay", "name": "CleanDeerRoll",
"form": { "form": {
"prologue": "您好我是AGI方向的猎头了解到您是这方面的大佬然后冒昧的就联系到您。这边有个机会想和您分享RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣" "prologue": "您好我是AGI方向的猎头了解到您是这方面的大佬然后冒昧的就联系到您。这边有个机会想和您分享RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣"
} }
@ -207,7 +207,7 @@
}, },
"data": { "data": {
"label": "Answer", "label": "Answer",
"name": "SolidRatsBurn", "name": "TwentyTimesDrive",
"form": {} "form": {}
}, },
"sourcePosition": "left", "sourcePosition": "left",
@ -222,7 +222,7 @@
}, },
"data": { "data": {
"label": "Categorize", "label": "Categorize",
"name": "ShyHousesAccept", "name": "StupidFoxesNail",
"form": { "form": {
"llm_id": "deepseek-chat", "llm_id": "deepseek-chat",
"category_description": { "category_description": {
@ -261,7 +261,7 @@
}, },
"data": { "data": {
"label": "Message", "label": "Message",
"name": "ThreeRegionsGrow", "name": "FluffyLightsCry",
"form": { "form": {
"messages": [ "messages": [
"我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAGRetrieval-Augmented Generation引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程结合大语言模型LLM针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的" "我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAGRetrieval-Augmented Generation引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程结合大语言模型LLM针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的"
@ -280,7 +280,7 @@
}, },
"data": { "data": {
"label": "Answer", "label": "Answer",
"name": "FancyChickenCut", "name": "BreezyJarsNotice",
"form": {} "form": {}
}, },
"sourcePosition": "left", "sourcePosition": "left",
@ -295,7 +295,7 @@
}, },
"data": { "data": {
"label": "Categorize", "label": "Categorize",
"name": "DeepAreasGrow", "name": "ItchyJobsLike",
"form": { "form": {
"llm_id": "deepseek-chat", "llm_id": "deepseek-chat",
"category_description": { "category_description": {
@ -335,7 +335,7 @@
}, },
"data": { "data": {
"label": "Generate", "label": "Generate",
"name": "TinyOlivesMelt", "name": "SocialLlamasDraw",
"form": { "form": {
"llm_id": "deepseek-chat", "llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头现在候选人的聊了和职位无关的话题请耐心的回应候选人并将话题往该AGI的职位上带最好能要到候选人微信号以便后面保持联系。", "prompt": "你是AGI方向的猎头现在候选人的聊了和职位无关的话题请耐心的回应候选人并将话题往该AGI的职位上带最好能要到候选人微信号以便后面保持联系。",
@ -356,7 +356,7 @@
}, },
"data": { "data": {
"label": "Retrieval", "label": "Retrieval",
"name": "SlimyDonkeysHug", "name": "ThinCamerasPunch",
"form": { "form": {
"similarity_threshold": 0.2, "similarity_threshold": 0.2,
"keywords_similarity_weight": 0.3, "keywords_similarity_weight": 0.3,
@ -378,7 +378,7 @@
}, },
"data": { "data": {
"label": "Generate", "label": "Generate",
"name": "PetiteHoundsMove", "name": "FiveAnimalsWorry",
"form": { "form": {
"llm_id": "deepseek-chat", "llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头候选人问了有关职位或公司的问题你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号\n - 方便加一下微信吗我把JD发您看看\n - 微信号多少我把详细职位JD发您\n 职位信息如下:\n {input}\n 职位信息如上。", "prompt": "你是AGI方向的猎头候选人问了有关职位或公司的问题你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号\n - 方便加一下微信吗我把JD发您看看\n - 微信号多少我把详细职位JD发您\n 职位信息如下:\n {input}\n 职位信息如上。",
@ -397,7 +397,7 @@
}, },
"data": { "data": {
"label": "Generate", "label": "Generate",
"name": "EagerSteaksWin", "name": "TastyTermsCarry",
"form": { "form": {
"llm_id": "deepseek-chat", "llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头候选人表示不反感加微信如果对方已经报了微信号表示感谢和信任并表示马上会加上如果没有则问对方微信号多少。你的微信号是weixin_kevinE-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", "prompt": "你是AGI方向的猎头候选人表示不反感加微信如果对方已经报了微信号表示感谢和信任并表示马上会加上如果没有则问对方微信号多少。你的微信号是weixin_kevinE-mail是kkk@ragflow.com。说话不要重复。不要总是您好。",
@ -418,7 +418,7 @@
}, },
"data": { "data": {
"label": "Generate", "label": "Generate",
"name": "IcyAntsBet", "name": "SharpYaksShout",
"form": { "form": {
"llm_id": "deepseek-chat", "llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人表示对于保护隐私信息给予理解也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevinE-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", "prompt": "你是AGI方向的猎头当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人表示对于保护隐私信息给予理解也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevinE-mail是kkk@ragflow.com。说话不要重复。不要总是您好。",
@ -439,7 +439,7 @@
}, },
"data": { "data": {
"label": "Message", "label": "Message",
"name": "SmallCarsRoll", "name": "ShaggyKeysStare",
"form": { "form": {
"messages": [ "messages": [
"好的,祝您生活愉快,工作顺利。", "好的,祝您生活愉快,工作顺利。",

View File

@ -96,6 +96,7 @@ export const useHandleDrop = () => {
}, },
data: { data: {
label: `${type}`, label: `${type}`,
name: humanId(),
}, },
sourcePosition: Position.Right, sourcePosition: Position.Right,
targetPosition: Position.Left, targetPosition: Position.Left,