### What problem does this PR solve? Feat: Use memo to wrap canvas nodes to improve fluency #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.1
| import { getLLMIconName, getLlmNameAndFIdByLlmId } from '@/utils/llm-util'; | import { getLLMIconName, getLlmNameAndFIdByLlmId } from '@/utils/llm-util'; | ||||
| import { memo } from 'react'; | |||||
| import { LlmIcon } from '../svg-icon'; | import { LlmIcon } from '../svg-icon'; | ||||
| interface IProps { | interface IProps { | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default LLMLabel; | |||||
| export default memo(LLMLabel); |
| import { LlmModelType } from '@/constants/knowledge'; | import { LlmModelType } from '@/constants/knowledge'; | ||||
| import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; | import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; | ||||
| import * as SelectPrimitive from '@radix-ui/react-select'; | import * as SelectPrimitive from '@radix-ui/react-select'; | ||||
| import { forwardRef, useState } from 'react'; | |||||
| import { forwardRef, memo, useState } from 'react'; | |||||
| import { LlmSettingFieldItems } from '../llm-setting-items/next'; | import { LlmSettingFieldItems } from '../llm-setting-items/next'; | ||||
| import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; | import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; | ||||
| import { Select, SelectTrigger, SelectValue } from '../ui/select'; | import { Select, SelectTrigger, SelectValue } from '../ui/select'; | ||||
| disabled?: boolean; | disabled?: boolean; | ||||
| } | } | ||||
| export const NextLLMSelect = forwardRef< | |||||
| const NextInnerLLMSelect = forwardRef< | |||||
| React.ElementRef<typeof SelectPrimitive.Trigger>, | React.ElementRef<typeof SelectPrimitive.Trigger>, | ||||
| IProps | IProps | ||||
| >(({ value, disabled }, ref) => { | >(({ value, disabled }, ref) => { | ||||
| ); | ); | ||||
| }); | }); | ||||
| NextLLMSelect.displayName = 'LLMSelect'; | |||||
| NextInnerLLMSelect.displayName = 'LLMSelect'; | |||||
| export const NextLLMSelect = memo(NextInnerLLMSelect); |
| SidebarHeader, | SidebarHeader, | ||||
| SidebarMenu, | SidebarMenu, | ||||
| } from '@/components/ui/sidebar'; | } from '@/components/ui/sidebar'; | ||||
| import { useMemo } from 'react'; | |||||
| import { memo, useMemo } from 'react'; | |||||
| import { | import { | ||||
| AgentOperatorList, | AgentOperatorList, | ||||
| Operator, | Operator, | ||||
| ); | ); | ||||
| } | } | ||||
| export function AgentSidebar() { | |||||
| function InnerAgentSidebar() { | |||||
| const agentOperatorList = useMemo(() => { | const agentOperatorList = useMemo(() => { | ||||
| return componentMenuList.filter((x) => | return componentMenuList.filter((x) => | ||||
| AgentOperatorList.some((y) => y === x.name), | AgentOperatorList.some((y) => y === x.name), | ||||
| </Sidebar> | </Sidebar> | ||||
| ); | ); | ||||
| } | } | ||||
| export const AgentSidebar = memo(InnerAgentSidebar); |
| import styles from './index.less'; | import styles from './index.less'; | ||||
| // TODO: do not allow other nodes to connect to this node | // TODO: do not allow other nodes to connect to this node | ||||
| function Node({ selected, data }: NodeProps<IBeginNode>) { | |||||
| function InnerBeginNode({ selected, data }: NodeProps<IBeginNode>) { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const query: BeginQuery[] = get(data, 'form.query', []); | const query: BeginQuery[] = get(data, 'form.query', []); | ||||
| const { theme } = useTheme(); | const { theme } = useTheme(); | ||||
| ); | ); | ||||
| } | } | ||||
| export const BeginNode = memo(Node); | |||||
| export const BeginNode = memo(InnerBeginNode); |
| import { Handle, Position } from '@xyflow/react'; | import { Handle, Position } from '@xyflow/react'; | ||||
| import React from 'react'; | |||||
| import React, { memo } from 'react'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| const DEFAULT_HANDLE_STYLE = { | const DEFAULT_HANDLE_STYLE = { | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default CategorizeHandle; | |||||
| export default memo(CategorizeHandle); |
| import { Flex } from 'antd'; | import { Flex } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo } from 'react'; | |||||
| import { RightHandleStyle } from './handle-icon'; | import { RightHandleStyle } from './handle-icon'; | ||||
| import { useBuildCategorizeHandlePositions } from './hooks'; | import { useBuildCategorizeHandlePositions } from './hooks'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function CategorizeNode({ | |||||
| export function InnerCategorizeNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| selected, | selected, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const CategorizeNode = memo(InnerCategorizeNode); |
| import { Handle, NodeProps, Position } from '@xyflow/react'; | import { Handle, NodeProps, Position } from '@xyflow/react'; | ||||
| import { Flex } from 'antd'; | import { Flex } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useState } from 'react'; | |||||
| import { memo, useState } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function EmailNode({ | |||||
| export function InnerEmailNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const EmailNode = memo(InnerEmailNode); |
| import { Handle, NodeProps, Position } from '@xyflow/react'; | import { Handle, NodeProps, Position } from '@xyflow/react'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function GenerateNode({ | |||||
| export function InnerGenerateNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const GenerateNode = memo(InnerGenerateNode); |
| import { IRagNode } from '@/interfaces/database/flow'; | import { IRagNode } from '@/interfaces/database/flow'; | ||||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | import { Handle, NodeProps, Position } from '@xyflow/react'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { memo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function RagNode({ | |||||
| function InnerRagNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const RagNode = memo(InnerRagNode); |
| import { Flex } from 'antd'; | import { Flex } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo } from 'react'; | |||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function InvokeNode({ | |||||
| function InnerInvokeNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const InvokeNode = memo(InnerInvokeNode); |
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||
| import { Handle, NodeProps, NodeResizeControl, Position } from '@xyflow/react'; | import { Handle, NodeProps, NodeResizeControl, Position } from '@xyflow/react'; | ||||
| import { ListRestart } from 'lucide-react'; | import { ListRestart } from 'lucide-react'; | ||||
| import { memo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| cursor: 'nwse-resize', | cursor: 'nwse-resize', | ||||
| }; | }; | ||||
| export function IterationNode({ | |||||
| export function InnerIterationNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| ); | ); | ||||
| } | } | ||||
| export function IterationStartNode({ | |||||
| function InnerIterationStartNode({ | |||||
| isConnectable = true, | isConnectable = true, | ||||
| selected, | selected, | ||||
| }: NodeProps<IIterationStartNode>) { | }: NodeProps<IIterationStartNode>) { | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const IterationStartNode = memo(InnerIterationStartNode); | |||||
| export const IterationNode = memo(InnerIterationNode); |
| import { Handle, NodeProps, Position } from '@xyflow/react'; | import { Handle, NodeProps, Position } from '@xyflow/react'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function KeywordNode({ | |||||
| export function InnerKeywordNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const KeywordNode = memo(InnerKeywordNode); |
| import { ILogicNode } from '@/interfaces/database/flow'; | import { ILogicNode } from '@/interfaces/database/flow'; | ||||
| import { Handle, NodeProps, Position } from '@xyflow/react'; | import { Handle, NodeProps, Position } from '@xyflow/react'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { memo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function LogicNode({ | |||||
| export function InnerLogicNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const LogicNode = memo(InnerLogicNode); |
| import { Flex } from 'antd'; | import { Flex } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function MessageNode({ | |||||
| function InnerMessageNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const MessageNode = memo(InnerMessageNode); |
| import NodeDropdown from './dropdown'; | import NodeDropdown from './dropdown'; | ||||
| import { NextNodePopover } from './popover'; | import { NextNodePopover } from './popover'; | ||||
| import { memo } from 'react'; | |||||
| import { RunTooltip } from '../../flow-tooltip'; | import { RunTooltip } from '../../flow-tooltip'; | ||||
| interface IProps { | interface IProps { | ||||
| id: string; | id: string; | ||||
| ); | ); | ||||
| } | } | ||||
| const NodeHeader = ({ | |||||
| const InnerNodeHeader = ({ | |||||
| label, | label, | ||||
| id, | id, | ||||
| name, | name, | ||||
| ); | ); | ||||
| }; | }; | ||||
| const NodeHeader = memo(InnerNodeHeader); | |||||
| export default NodeHeader; | export default NodeHeader; |
| import { useTheme } from '@/components/theme-provider'; | import { useTheme } from '@/components/theme-provider'; | ||||
| import { IRelevantNode } from '@/interfaces/database/flow'; | import { IRelevantNode } from '@/interfaces/database/flow'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo } from 'react'; | |||||
| import { useReplaceIdWithName } from '../../hooks'; | import { useReplaceIdWithName } from '../../hooks'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function RelevantNode({ id, data, selected }: NodeProps<IRelevantNode>) { | |||||
| function InnerRelevantNode({ id, data, selected }: NodeProps<IRelevantNode>) { | |||||
| const yes = get(data, 'form.yes'); | const yes = get(data, 'form.yes'); | ||||
| const no = get(data, 'form.no'); | const no = get(data, 'form.no'); | ||||
| const replaceIdWithName = useReplaceIdWithName(); | const replaceIdWithName = useReplaceIdWithName(); | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const RelevantNode = memo(InnerRelevantNode); |
| import { Avatar, Flex } from 'antd'; | import { Avatar, Flex } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { useMemo } from 'react'; | |||||
| import { memo, useMemo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function RetrievalNode({ | |||||
| function InnerRetrievalNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const RetrievalNode = memo(InnerRetrievalNode); |
| import { Handle, NodeProps, Position } from '@xyflow/react'; | import { Handle, NodeProps, Position } from '@xyflow/react'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { memo } from 'react'; | |||||
| import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| export function RewriteNode({ | |||||
| function InnerRewriteNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const RewriteNode = memo(InnerRewriteNode); |
| import { Handle, NodeProps, Position } from '@xyflow/react'; | import { Handle, NodeProps, Position } from '@xyflow/react'; | ||||
| import { Divider, Flex } from 'antd'; | import { Divider, Flex } from 'antd'; | ||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { memo } from 'react'; | |||||
| import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query'; | import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query'; | ||||
| import { RightHandleStyle } from './handle-icon'; | import { RightHandleStyle } from './handle-icon'; | ||||
| import { useBuildSwitchHandlePositions } from './hooks'; | import { useBuildSwitchHandlePositions } from './hooks'; | ||||
| ); | ); | ||||
| }; | }; | ||||
| export function SwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) { | |||||
| function InnerSwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) { | |||||
| const { positions } = useBuildSwitchHandlePositions({ data, id }); | const { positions } = useBuildSwitchHandlePositions({ data, id }); | ||||
| const { theme } = useTheme(); | const { theme } = useTheme(); | ||||
| return ( | return ( | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const SwitchNode = memo(InnerSwitchNode); |
| import NodeHeader from './node-header'; | import NodeHeader from './node-header'; | ||||
| import { ITemplateNode } from '@/interfaces/database/flow'; | import { ITemplateNode } from '@/interfaces/database/flow'; | ||||
| import { memo } from 'react'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| export function TemplateNode({ | |||||
| function InnerTemplateNode({ | |||||
| id, | id, | ||||
| data, | data, | ||||
| isConnectable = true, | isConnectable = true, | ||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } | ||||
| export const TemplateNode = memo(InnerTemplateNode); |