import type { MouseEvent } from 'react' import { memo, useCallback, useEffect, useState, } from 'react' import { Handle, Position, } from 'reactflow' import { useTranslation } from 'react-i18next' import { BlockEnum } from '../../../types' import type { Node } from '../../../types' import BlockSelector from '../../../block-selector' import type { ToolDefaultValue } from '../../../block-selector/types' import { useAvailableBlocks, useIsChatMode, useNodesInteractions, useNodesReadOnly, useWorkflow, } from '../../../hooks' import { useStore, } from '../../../store' type NodeHandleProps = { handleId: string handleClassName?: string nodeSelectorClassName?: string } & Pick export const NodeTargetHandle = memo(({ id, data, handleId, handleClassName, nodeSelectorClassName, }: NodeHandleProps) => { const [open, setOpen] = useState(false) const { handleNodeAdd } = useNodesInteractions() const { getNodesReadOnly } = useNodesReadOnly() const connected = data._connectedTargetHandleIds?.includes(handleId) const { availablePrevBlocks } = useAvailableBlocks(data.type, data.isInIteration) const isConnectable = !!availablePrevBlocks.length const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) const handleHandleClick = useCallback((e: MouseEvent) => { e.stopPropagation() if (!connected) setOpen(v => !v) }, [connected]) const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => { handleNodeAdd( { nodeType: type, toolDefaultValue, }, { nextNodeId: id, nextNodeTargetHandle: handleId, }, ) }, [handleNodeAdd, id, handleId]) return ( <> { !connected && isConnectable && !getNodesReadOnly() && ( ` hidden absolute left-0 top-0 pointer-events-none ${nodeSelectorClassName} group-hover:!flex ${data.selected && '!flex'} ${open && '!flex'} `} availableBlocksTypes={availablePrevBlocks} /> ) } ) }) NodeTargetHandle.displayName = 'NodeTargetHandle' export const NodeSourceHandle = memo(({ id, data, handleId, handleClassName, nodeSelectorClassName, }: NodeHandleProps) => { const { t } = useTranslation() const notInitialWorkflow = useStore(s => s.notInitialWorkflow) const [open, setOpen] = useState(false) const { handleNodeAdd } = useNodesInteractions() const { getNodesReadOnly } = useNodesReadOnly() const { availableNextBlocks } = useAvailableBlocks(data.type, data.isInIteration) const isConnectable = !!availableNextBlocks.length const isChatMode = useIsChatMode() const { checkParallelLimit } = useWorkflow() const connected = data._connectedSourceHandleIds?.includes(handleId) const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) const handleHandleClick = useCallback((e: MouseEvent) => { e.stopPropagation() if (checkParallelLimit(id, handleId)) setOpen(v => !v) }, [checkParallelLimit, id, handleId]) const handleSelect = useCallback((type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => { handleNodeAdd( { nodeType: type, toolDefaultValue, }, { prevNodeId: id, prevNodeSourceHandle: handleId, }, ) }, [handleNodeAdd, id, handleId]) useEffect(() => { if (notInitialWorkflow && data.type === BlockEnum.Start && !isChatMode) setOpen(true) }, [notInitialWorkflow, data.type, isChatMode]) return (
{t('workflow.common.parallelTip.click.title')} {t('workflow.common.parallelTip.click.desc')}
{t('workflow.common.parallelTip.drag.title')} {t('workflow.common.parallelTip.drag.desc')}
{ isConnectable && !getNodesReadOnly() && ( ` hidden absolute top-0 left-0 pointer-events-none ${nodeSelectorClassName} group-hover:!flex ${data.selected && '!flex'} ${open && '!flex'} `} availableBlocksTypes={availableNextBlocks} /> ) }
) }) NodeSourceHandle.displayName = 'NodeSourceHandle'