import type { FC, ReactElement, } from 'react' import type { NodeProps } from '@/app/components/workflow/types' import { cloneElement, memo, useMemo, useRef, } from 'react' import { useTranslation } from 'react-i18next' import BlockIcon from '@/app/components/workflow/block-icon' import { ToolTypeEnum } from '@/app/components/workflow/block-selector/types' import { useNodesReadOnly, useToolIcon } from '@/app/components/workflow/hooks' import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' import { useNodeIterationInteractions } from '@/app/components/workflow/nodes/iteration/use-interactions' import { useNodeLoopInteractions } from '@/app/components/workflow/nodes/loop/use-interactions' import CopyID from '@/app/components/workflow/nodes/tool/components/copy-id' import { BlockEnum, NodeRunningStatus, } from '@/app/components/workflow/types' import { hasErrorHandleNode, hasRetryNode } from '@/app/components/workflow/utils' import { cn } from '@/utils/classnames' import AddVariablePopupWithPosition from './components/add-variable-popup-with-position' import EntryNodeContainer, { StartNodeTypeEnum } from './components/entry-node-container' import ErrorHandleOnNode from './components/error-handle/error-handle-on-node' import NodeControl from './components/node-control' import { NodeSourceHandle, NodeTargetHandle, } from './components/node-handle' import NodeResizer from './components/node-resizer' import RetryOnNode from './components/retry/retry-on-node' import { NodeBody, NodeDescription, NodeHeaderMeta, } from './node-sections' import { getLoopIndexTextKey, getNodeStatusBorders, isContainerNode, isEntryWorkflowNode, } from './node.helpers' import useNodeResizeObserver from './use-node-resize-observer' type NodeChildProps = { id: string data: NodeProps['data'] } type BaseNodeProps = { children: ReactElement> id: NodeProps['id'] data: NodeProps['data'] } const BaseNode: FC = ({ id, data, children, }) => { const { t } = useTranslation() const nodeRef = useRef(null) const { nodesReadOnly } = useNodesReadOnly() const { handleNodeIterationChildSizeChange } = useNodeIterationInteractions() const { handleNodeLoopChildSizeChange } = useNodeLoopInteractions() const toolIcon = useToolIcon(data) const { shouldDim: pluginDimmed, isChecking: pluginIsChecking, isMissing: pluginIsMissing, canInstall: pluginCanInstall, uniqueIdentifier: pluginUniqueIdentifier } = useNodePluginInstallation(data) const pluginInstallLocked = !pluginIsChecking && pluginIsMissing && pluginCanInstall && Boolean(pluginUniqueIdentifier) useNodeResizeObserver({ enabled: Boolean(data.selected && data.isInIteration), nodeRef, onResize: () => handleNodeIterationChildSizeChange(id), }) useNodeResizeObserver({ enabled: Boolean(data.selected && data.isInLoop), nodeRef, onResize: () => handleNodeLoopChildSizeChange(id), }) const { hasNodeInspectVars } = useInspectVarsCrud() const isLoading = data._runningStatus === NodeRunningStatus.Running || data._singleRunningStatus === NodeRunningStatus.Running const hasVarValue = hasNodeInspectVars(id) const showSelectedBorder = Boolean(data.selected || data._isBundled || data._isEntering) const { showRunningBorder, showSuccessBorder, showFailedBorder, showExceptionBorder, } = useMemo(() => getNodeStatusBorders(data._runningStatus, hasVarValue, showSelectedBorder), [data._runningStatus, hasVarValue, showSelectedBorder]) const LoopIndex = useMemo(() => { const translationKey = getLoopIndexTextKey(data._runningStatus) const text = translationKey ? t(translationKey, { ns: 'workflow', count: data._loopIndex }) : '' if (text) { return (
{text}
) } return null }, [data._loopIndex, data._runningStatus, t]) const nodeContent = (
{(data._dimmed || pluginDimmed || pluginInstallLocked) && (
e.stopPropagation() : undefined} data-testid="workflow-node-install-overlay" /> )} { data.type === BlockEnum.DataSource && (
{t('blocks.datasource', { ns: 'workflow' })}
) }
{ data._showAddVariablePopup && ( ) } { data.type === BlockEnum.Iteration && ( ) } { data.type === BlockEnum.Loop && ( ) } { !data._isCandidate && ( ) } { data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && data.type !== BlockEnum.HumanInput && !data._isCandidate && ( ) } { !data._runningStatus && !nodesReadOnly && !data._isCandidate && ( ) }
{data.title}
{ hasRetryNode(data.type) && ( ) } { hasErrorHandleNode(data.type) && ( ) } {data.type === BlockEnum.Tool && data.provider_type === ToolTypeEnum.MCP && (
)}
) const isStartNode = data.type === BlockEnum.Start const isEntryNode = isEntryWorkflowNode(data.type) return isEntryNode ? ( {nodeContent} ) : nodeContent } export default memo(BaseNode)