| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- 'use client'
- import type { FC } from 'react'
- import React, { useCallback, useEffect, useMemo, useRef } from 'react'
- import cn from '@/utils/classnames'
- import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
- import { useGetLanguage } from '@/context/i18n'
- import type { Tool as ToolType } from '../../../tools/types'
- import { CollectionType } from '../../../tools/types'
- import type { ToolWithProvider } from '../../types'
- import { BlockEnum } from '../../types'
- import type { ToolDefaultValue, ToolValue } from '../types'
- import { ViewType } from '../view-type-select'
- import ActionItem from './action-item'
- import BlockIcon from '../../block-icon'
- import { useTranslation } from 'react-i18next'
- import { useHover } from 'ahooks'
- import McpToolNotSupportTooltip from '../../nodes/_base/components/mcp-tool-not-support-tooltip'
- import { Mcp } from '@/app/components/base/icons/src/vender/other'
-
- type Props = {
- className?: string
- payload: ToolWithProvider
- viewType: ViewType
- hasSearchText: boolean
- onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void
- canNotSelectMultiple?: boolean
- onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void
- selectedTools?: ToolValue[]
- canChooseMCPTool?: boolean
- }
-
- const Tool: FC<Props> = ({
- className,
- payload,
- viewType,
- hasSearchText,
- onSelect,
- canNotSelectMultiple,
- onSelectMultiple,
- selectedTools,
- canChooseMCPTool,
- }) => {
- const { t } = useTranslation()
- const language = useGetLanguage()
- const isFlatView = viewType === ViewType.flat
- const notShowProvider = payload.type === CollectionType.workflow
- const actions = payload.tools
- const hasAction = !notShowProvider
- const [isFold, setFold] = React.useState<boolean>(true)
- const ref = useRef(null)
- const isHovering = useHover(ref)
- const isMCPTool = payload.type === CollectionType.mcp
- const isShowCanNotChooseMCPTip = !canChooseMCPTool && isMCPTool
- const getIsDisabled = useCallback((tool: ToolType) => {
- if (!selectedTools || !selectedTools.length) return false
- return selectedTools.some(selectedTool => (selectedTool.provider_name === payload.name || selectedTool.provider_name === payload.id) && selectedTool.tool_name === tool.name)
- }, [payload.id, payload.name, selectedTools])
-
- const totalToolsNum = actions.length
- const selectedToolsNum = actions.filter(action => getIsDisabled(action)).length
- const isAllSelected = selectedToolsNum === totalToolsNum
-
- const notShowProviderSelectInfo = useMemo(() => {
- if (isAllSelected) {
- return (
- <span className='system-xs-regular text-text-tertiary'>
- {t('tools.addToolModal.added')}
- </span>
- )
- }
- }, [isAllSelected, t])
- const selectedInfo = useMemo(() => {
- if (isHovering && !isAllSelected) {
- return (
- <span className='system-xs-regular text-components-button-secondary-accent-text'
- onClick={(e) => {
- onSelectMultiple?.(BlockEnum.Tool, actions.filter(action => !getIsDisabled(action)).map((tool) => {
- const params: Record<string, string> = {}
- if (tool.parameters) {
- tool.parameters.forEach((item) => {
- params[item.name] = ''
- })
- }
- return {
- provider_id: payload.id,
- provider_type: payload.type,
- provider_name: payload.name,
- tool_name: tool.name,
- tool_label: tool.label[language],
- tool_description: tool.description[language],
- title: tool.label[language],
- is_team_authorization: payload.is_team_authorization,
- output_schema: tool.output_schema,
- paramSchemas: tool.parameters,
- params,
- }
- }))
- }}
- >
- {t('workflow.tabs.addAll')}
- </span>
- )
- }
-
- if (selectedToolsNum === 0)
- return <></>
-
- return (
- <span className='system-xs-regular text-text-tertiary'>
- {isAllSelected
- ? t('workflow.tabs.allAdded')
- : `${selectedToolsNum} / ${totalToolsNum}`
- }
- </span>
- )
- }, [actions, getIsDisabled, isAllSelected, isHovering, language, onSelectMultiple, payload.id, payload.is_team_authorization, payload.name, payload.type, selectedToolsNum, t, totalToolsNum])
-
- useEffect(() => {
- if (hasSearchText && isFold) {
- setFold(false)
- return
- }
- if (!hasSearchText && !isFold)
- setFold(true)
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [hasSearchText])
-
- const FoldIcon = isFold ? RiArrowRightSLine : RiArrowDownSLine
-
- const groupName = useMemo(() => {
- if (payload.type === CollectionType.builtIn)
- return payload.author
-
- if (payload.type === CollectionType.custom)
- return t('workflow.tabs.customTool')
-
- if (payload.type === CollectionType.workflow)
- return t('workflow.tabs.workflowTool')
-
- return ''
- }, [payload.author, payload.type, t])
-
- return (
- <div
- key={payload.id}
- className={cn('mb-1 last-of-type:mb-0')}
- ref={ref}
- >
- <div className={cn(className)}>
- <div
- className='group/item flex w-full cursor-pointer select-none items-center justify-between rounded-lg pl-3 pr-1 hover:bg-state-base-hover'
- onClick={() => {
- if (hasAction) {
- setFold(!isFold)
- return
- }
-
- const tool = actions[0]
- const params: Record<string, string> = {}
- if (tool.parameters) {
- tool.parameters.forEach((item) => {
- params[item.name] = ''
- })
- }
- onSelect(BlockEnum.Tool, {
- provider_id: payload.id,
- provider_type: payload.type,
- provider_name: payload.name,
- tool_name: tool.name,
- tool_label: tool.label[language],
- tool_description: tool.description[language],
- title: tool.label[language],
- is_team_authorization: payload.is_team_authorization,
- output_schema: tool.output_schema,
- paramSchemas: tool.parameters,
- params,
- })
- }}
- >
- <div className={cn('flex h-8 grow items-center', isShowCanNotChooseMCPTip && 'opacity-30')}>
- <BlockIcon
- className='shrink-0'
- type={BlockEnum.Tool}
- toolIcon={payload.icon}
- />
- <div className='ml-2 flex w-0 grow items-center text-sm text-text-primary'>
- <span className='max-w-[250px] truncate'>{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span>
- {isFlatView && groupName && (
- <span className='system-xs-regular ml-2 shrink-0 text-text-quaternary'>{groupName}</span>
- )}
- {isMCPTool && <Mcp className='ml-2 size-3.5 shrink-0 text-text-quaternary' />}
- </div>
- </div>
-
- <div className='ml-2 flex items-center'>
- {!isShowCanNotChooseMCPTip && !canNotSelectMultiple && (notShowProvider ? notShowProviderSelectInfo : selectedInfo)}
- {isShowCanNotChooseMCPTip && <McpToolNotSupportTooltip />}
- {hasAction && (
- <FoldIcon className={cn('h-4 w-4 shrink-0 text-text-tertiary group-hover/item:text-text-tertiary', isFold && 'text-text-quaternary')} />
- )}
- </div>
- </div>
-
- {!notShowProvider && hasAction && !isFold && (
- actions.map(action => (
- <ActionItem
- key={action.name}
- provider={payload}
- payload={action}
- onSelect={onSelect}
- disabled={getIsDisabled(action) || isShowCanNotChooseMCPTip}
- isAdded={getIsDisabled(action)}
- />
- ))
- )}
- </div>
- </div>
- )
- }
- export default React.memo(Tool)
|