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.

all-tools.tsx 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {
  2. useEffect,
  3. useMemo,
  4. useRef,
  5. useState,
  6. } from 'react'
  7. import type {
  8. BlockEnum,
  9. OnSelectBlock,
  10. ToolWithProvider,
  11. } from '../types'
  12. import type { ToolDefaultValue, ToolValue } from './types'
  13. import { ToolTypeEnum } from './types'
  14. import Tools from './tools'
  15. import { useToolTabs } from './hooks'
  16. import ViewTypeSelect, { ViewType } from './view-type-select'
  17. import cn from '@/utils/classnames'
  18. import { useGetLanguage } from '@/context/i18n'
  19. import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list'
  20. import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list'
  21. import { PluginType } from '../../plugins/types'
  22. import { useMarketplacePlugins } from '../../plugins/marketplace/hooks'
  23. import { useGlobalPublicStore } from '@/context/global-public-context'
  24. type AllToolsProps = {
  25. className?: string
  26. toolContentClassName?: string
  27. searchText: string
  28. tags: ListProps['tags']
  29. buildInTools: ToolWithProvider[]
  30. customTools: ToolWithProvider[]
  31. workflowTools: ToolWithProvider[]
  32. mcpTools: ToolWithProvider[]
  33. onSelect: OnSelectBlock
  34. canNotSelectMultiple?: boolean
  35. onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void
  36. selectedTools?: ToolValue[]
  37. canChooseMCPTool?: boolean
  38. }
  39. const DEFAULT_TAGS: AllToolsProps['tags'] = []
  40. const AllTools = ({
  41. className,
  42. toolContentClassName,
  43. searchText,
  44. tags = DEFAULT_TAGS,
  45. onSelect,
  46. canNotSelectMultiple,
  47. onSelectMultiple,
  48. buildInTools,
  49. workflowTools,
  50. customTools,
  51. mcpTools = [],
  52. selectedTools,
  53. canChooseMCPTool,
  54. }: AllToolsProps) => {
  55. const language = useGetLanguage()
  56. const tabs = useToolTabs()
  57. const [activeTab, setActiveTab] = useState(ToolTypeEnum.All)
  58. const [activeView, setActiveView] = useState<ViewType>(ViewType.flat)
  59. const hasFilter = searchText || tags.length > 0
  60. const isMatchingKeywords = (text: string, keywords: string) => {
  61. return text.toLowerCase().includes(keywords.toLowerCase())
  62. }
  63. const tools = useMemo(() => {
  64. let mergedTools: ToolWithProvider[] = []
  65. if (activeTab === ToolTypeEnum.All)
  66. mergedTools = [...buildInTools, ...customTools, ...workflowTools, ...mcpTools]
  67. if (activeTab === ToolTypeEnum.BuiltIn)
  68. mergedTools = buildInTools
  69. if (activeTab === ToolTypeEnum.Custom)
  70. mergedTools = customTools
  71. if (activeTab === ToolTypeEnum.Workflow)
  72. mergedTools = workflowTools
  73. if (activeTab === ToolTypeEnum.MCP)
  74. mergedTools = mcpTools
  75. if (!hasFilter)
  76. return mergedTools.filter(toolWithProvider => toolWithProvider.tools.length > 0)
  77. return mergedTools.filter((toolWithProvider) => {
  78. return isMatchingKeywords(toolWithProvider.name, searchText) || toolWithProvider.tools.some((tool) => {
  79. return tool.label[language].toLowerCase().includes(searchText.toLowerCase()) || tool.name.toLowerCase().includes(searchText.toLowerCase())
  80. })
  81. })
  82. }, [activeTab, buildInTools, customTools, workflowTools, mcpTools, searchText, language, hasFilter])
  83. const {
  84. queryPluginsWithDebounced: fetchPlugins,
  85. plugins: notInstalledPlugins = [],
  86. } = useMarketplacePlugins()
  87. const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
  88. useEffect(() => {
  89. if (!enable_marketplace) return
  90. if (searchText || tags.length > 0) {
  91. fetchPlugins({
  92. query: searchText,
  93. tags,
  94. category: PluginType.tool,
  95. })
  96. }
  97. // eslint-disable-next-line react-hooks/exhaustive-deps
  98. }, [searchText, tags, enable_marketplace])
  99. const pluginRef = useRef<ListRef>(null)
  100. const wrapElemRef = useRef<HTMLDivElement>(null)
  101. const isSupportGroupView = [ToolTypeEnum.All, ToolTypeEnum.BuiltIn].includes(activeTab)
  102. return (
  103. <div className={cn('min-w-[400px] max-w-[500px]', className)}>
  104. <div className='flex items-center justify-between border-b border-divider-subtle px-3'>
  105. <div className='flex h-8 items-center space-x-1'>
  106. {
  107. tabs.map(tab => (
  108. <div
  109. className={cn(
  110. 'flex h-6 cursor-pointer items-center rounded-md px-2 hover:bg-state-base-hover',
  111. 'text-xs font-medium text-text-secondary',
  112. activeTab === tab.key && 'bg-state-base-hover-alt',
  113. )}
  114. key={tab.key}
  115. onClick={() => setActiveTab(tab.key)}
  116. >
  117. {tab.name}
  118. </div>
  119. ))
  120. }
  121. </div>
  122. {isSupportGroupView && (
  123. <ViewTypeSelect viewType={activeView} onChange={setActiveView} />
  124. )}
  125. </div>
  126. <div
  127. ref={wrapElemRef}
  128. className='max-h-[464px] overflow-y-auto'
  129. onScroll={pluginRef.current?.handleScroll}
  130. >
  131. <Tools
  132. className={toolContentClassName}
  133. tools={tools}
  134. onSelect={onSelect}
  135. canNotSelectMultiple={canNotSelectMultiple}
  136. onSelectMultiple={onSelectMultiple}
  137. toolType={activeTab}
  138. viewType={isSupportGroupView ? activeView : ViewType.flat}
  139. hasSearchText={!!searchText}
  140. selectedTools={selectedTools}
  141. canChooseMCPTool={canChooseMCPTool}
  142. />
  143. {/* Plugins from marketplace */}
  144. {enable_marketplace && <PluginList
  145. ref={pluginRef}
  146. wrapElemRef={wrapElemRef}
  147. list={notInstalledPlugins}
  148. searchText={searchText}
  149. toolContentClassName={toolContentClassName}
  150. tags={tags}
  151. />}
  152. </div>
  153. </div>
  154. )
  155. }
  156. export default AllTools