You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tool-item.tsx 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. 'use client'
  2. import React, { useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import {
  5. RiDeleteBinLine,
  6. RiEqualizer2Line,
  7. RiErrorWarningFill,
  8. } from '@remixicon/react'
  9. import { Group } from '@/app/components/base/icons/src/vender/other'
  10. import AppIcon from '@/app/components/base/app-icon'
  11. import Switch from '@/app/components/base/switch'
  12. import Button from '@/app/components/base/button'
  13. import Indicator from '@/app/components/header/indicator'
  14. import ActionButton from '@/app/components/base/action-button'
  15. import Tooltip from '@/app/components/base/tooltip'
  16. import { ToolTipContent } from '@/app/components/base/tooltip/content'
  17. import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
  18. import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version'
  19. import cn from '@/utils/classnames'
  20. import McpToolNotSupportTooltip from '@/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip'
  21. type Props = {
  22. icon?: any
  23. providerName?: string
  24. isMCPTool?: boolean
  25. providerShowName?: string
  26. toolLabel?: string
  27. showSwitch?: boolean
  28. switchValue?: boolean
  29. onSwitchChange?: (value: boolean) => void
  30. onDelete?: () => void
  31. noAuth?: boolean
  32. isError?: boolean
  33. errorTip?: any
  34. uninstalled?: boolean
  35. installInfo?: string
  36. onInstall?: () => void
  37. versionMismatch?: boolean
  38. open: boolean
  39. authRemoved?: boolean
  40. canChooseMCPTool?: boolean,
  41. }
  42. const ToolItem = ({
  43. open,
  44. icon,
  45. isMCPTool,
  46. providerShowName,
  47. providerName,
  48. toolLabel,
  49. showSwitch,
  50. switchValue,
  51. onSwitchChange,
  52. onDelete,
  53. noAuth,
  54. uninstalled,
  55. installInfo,
  56. onInstall,
  57. isError,
  58. errorTip,
  59. versionMismatch,
  60. authRemoved,
  61. canChooseMCPTool,
  62. }: Props) => {
  63. const { t } = useTranslation()
  64. const providerNameText = isMCPTool ? providerShowName : providerName?.split('/').pop()
  65. const isTransparent = uninstalled || versionMismatch || isError
  66. const [isDeleting, setIsDeleting] = useState(false)
  67. const isShowCanNotChooseMCPTip = isMCPTool && !canChooseMCPTool
  68. return (
  69. <div className={cn(
  70. 'group flex cursor-default items-center gap-1 rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg p-1.5 pr-2 shadow-xs hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm',
  71. open && 'bg-components-panel-on-panel-item-bg-hover shadow-sm',
  72. isDeleting && 'border-state-destructive-border shadow-xs hover:bg-state-destructive-hover',
  73. )}>
  74. {icon && (
  75. <div className={cn('shrink-0', isTransparent && 'opacity-50', isShowCanNotChooseMCPTip && 'opacity-30')}>
  76. {typeof icon === 'string' && <div className='h-7 w-7 rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge bg-cover bg-center' style={{ backgroundImage: `url(${icon})` }} />}
  77. {typeof icon !== 'string' && <AppIcon className='h-7 w-7 rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge' size='xs' icon={icon?.content} background={icon?.background} />}
  78. </div>
  79. )}
  80. {!icon && (
  81. <div className={cn(
  82. 'flex h-7 w-7 items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle',
  83. isTransparent && 'opacity-50', isShowCanNotChooseMCPTip && 'opacity-30',
  84. )}>
  85. <div className='flex h-5 w-5 items-center justify-center opacity-35'>
  86. <Group className='text-text-tertiary' />
  87. </div>
  88. </div>
  89. )}
  90. <div className={cn('grow truncate pl-0.5', isTransparent && 'opacity-50', isShowCanNotChooseMCPTip && 'opacity-30')}>
  91. <div className='system-2xs-medium-uppercase text-text-tertiary'>{providerNameText}</div>
  92. <div className='system-xs-medium text-text-secondary'>{toolLabel}</div>
  93. </div>
  94. <div className='hidden items-center gap-1 group-hover:flex'>
  95. {!noAuth && !isError && !uninstalled && !versionMismatch && !isShowCanNotChooseMCPTip && (
  96. <ActionButton>
  97. <RiEqualizer2Line className='h-4 w-4' />
  98. </ActionButton>
  99. )}
  100. <div
  101. className='cursor-pointer rounded-md p-1 text-text-tertiary hover:text-text-destructive'
  102. onClick={(e) => {
  103. e.stopPropagation()
  104. onDelete?.()
  105. }}
  106. onMouseOver={() => setIsDeleting(true)}
  107. onMouseLeave={() => setIsDeleting(false)}
  108. >
  109. <RiDeleteBinLine className='h-4 w-4' />
  110. </div>
  111. </div>
  112. {!isError && !uninstalled && !noAuth && !versionMismatch && !isShowCanNotChooseMCPTip && showSwitch && (
  113. <div className='mr-1' onClick={e => e.stopPropagation()}>
  114. <Switch
  115. size='md'
  116. defaultValue={switchValue}
  117. onChange={onSwitchChange}
  118. />
  119. </div>
  120. )}
  121. {isShowCanNotChooseMCPTip && (
  122. <McpToolNotSupportTooltip />
  123. )}
  124. {!isError && !uninstalled && !versionMismatch && noAuth && (
  125. <Button variant='secondary' size='small'>
  126. {t('tools.notAuthorized')}
  127. <Indicator className='ml-2' color='orange' />
  128. </Button>
  129. )}
  130. {!isError && !uninstalled && !versionMismatch && authRemoved && (
  131. <Button variant='secondary' size='small'>
  132. {t('plugin.auth.authRemoved')}
  133. <Indicator className='ml-2' color='red' />
  134. </Button>
  135. )}
  136. {!isError && !uninstalled && versionMismatch && installInfo && (
  137. <div onClick={e => e.stopPropagation()}>
  138. <SwitchPluginVersion
  139. className='-mt-1'
  140. uniqueIdentifier={installInfo}
  141. tooltip={
  142. <ToolTipContent
  143. title={t('plugin.detailPanel.toolSelector.unsupportedTitle')}
  144. >
  145. {`${t('plugin.detailPanel.toolSelector.unsupportedContent')} ${t('plugin.detailPanel.toolSelector.unsupportedContent2')}`}
  146. </ToolTipContent>
  147. }
  148. onChange={() => {
  149. onInstall?.()
  150. }}
  151. />
  152. </div>
  153. )}
  154. {!isError && uninstalled && installInfo && (
  155. <InstallPluginButton
  156. onClick={e => e.stopPropagation()}
  157. size={'small'}
  158. uniqueIdentifier={installInfo}
  159. onSuccess={() => {
  160. onInstall?.()
  161. }}
  162. />
  163. )}
  164. {isError && (
  165. <Tooltip
  166. popupContent={errorTip}
  167. >
  168. <div>
  169. <RiErrorWarningFill className='h-4 w-4 text-text-destructive' />
  170. </div>
  171. </Tooltip>
  172. )}
  173. </div>
  174. )
  175. }
  176. export default ToolItem