refactor(web): remove redundant add-tool-modal components and related code (#27996)

This commit is contained in:
yangzheli
2025-11-13 20:21:04 +08:00
committed by GitHub
parent ffc04f2a9b
commit 20403c69b2
41 changed files with 2 additions and 800 deletions

View File

@@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="apps-2-line">
<path id="Vector" d="M4.66602 7.6665C3.00916 7.6665 1.66602 6.32336 1.66602 4.6665C1.66602 3.00965 3.00916 1.6665 4.66602 1.6665C6.32287 1.6665 7.66602 3.00965 7.66602 4.6665C7.66602 6.32336 6.32287 7.6665 4.66602 7.6665ZM4.66602 14.3332C3.00916 14.3332 1.66602 12.99 1.66602 11.3332C1.66602 9.6763 3.00916 8.33317 4.66602 8.33317C6.32287 8.33317 7.66602 9.6763 7.66602 11.3332C7.66602 12.99 6.32287 14.3332 4.66602 14.3332ZM11.3327 7.6665C9.67582 7.6665 8.33268 6.32336 8.33268 4.6665C8.33268 3.00965 9.67582 1.6665 11.3327 1.6665C12.9895 1.6665 14.3327 3.00965 14.3327 4.6665C14.3327 6.32336 12.9895 7.6665 11.3327 7.6665ZM11.3327 14.3332C9.67582 14.3332 8.33268 12.99 8.33268 11.3332C8.33268 9.6763 9.67582 8.33317 11.3327 8.33317C12.9895 8.33317 14.3327 9.6763 14.3327 11.3332C14.3327 12.99 12.9895 14.3332 11.3327 14.3332ZM4.66602 6.33317C5.58649 6.33317 6.33268 5.58698 6.33268 4.6665C6.33268 3.74603 5.58649 2.99984 4.66602 2.99984C3.74554 2.99984 2.99935 3.74603 2.99935 4.6665C2.99935 5.58698 3.74554 6.33317 4.66602 6.33317ZM4.66602 12.9998C5.58649 12.9998 6.33268 12.2536 6.33268 11.3332C6.33268 10.4127 5.58649 9.6665 4.66602 9.6665C3.74554 9.6665 2.99935 10.4127 2.99935 11.3332C2.99935 12.2536 3.74554 12.9998 4.66602 12.9998ZM11.3327 6.33317C12.2531 6.33317 12.9993 5.58698 12.9993 4.6665C12.9993 3.74603 12.2531 2.99984 11.3327 2.99984C10.4122 2.99984 9.66602 3.74603 9.66602 4.6665C9.66602 5.58698 10.4122 6.33317 11.3327 6.33317ZM11.3327 12.9998C12.2531 12.9998 12.9993 12.2536 12.9993 11.3332C12.9993 10.4127 12.2531 9.6665 11.3327 9.6665C10.4122 9.6665 9.66602 10.4127 9.66602 11.3332C9.66602 12.2536 10.4122 12.9998 11.3327 12.9998Z" fill="#155EEF"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.66602 14.3334C3.00916 14.3334 1.66602 12.9903 1.66602 11.3334C1.66602 9.67655 3.00916 8.33342 4.66602 8.33342C6.32287 8.33342 7.66602 9.67655 7.66602 11.3334C7.66602 12.9903 6.32287 14.3334 4.66602 14.3334ZM11.3327 7.66675C9.67582 7.66675 8.33268 6.3236 8.33268 4.66675C8.33268 3.00989 9.67582 1.66675 11.3327 1.66675C12.9895 1.66675 14.3327 3.00989 14.3327 4.66675C14.3327 6.3236 12.9895 7.66675 11.3327 7.66675ZM4.66602 13.0001C5.58649 13.0001 6.33268 12.2539 6.33268 11.3334C6.33268 10.4129 5.58649 9.66675 4.66602 9.66675C3.74554 9.66675 2.99935 10.4129 2.99935 11.3334C2.99935 12.2539 3.74554 13.0001 4.66602 13.0001ZM11.3327 6.33342C12.2531 6.33342 12.9993 5.58722 12.9993 4.66675C12.9993 3.74627 12.2531 3.00008 11.3327 3.00008C10.4122 3.00008 9.66602 3.74627 9.66602 4.66675C9.66602 5.58722 10.4122 6.33342 11.3327 6.33342ZM1.99935 5.33341C1.99935 3.49247 3.49174 2.00008 5.33268 2.00008H7.33268V3.33341H5.33268C4.22812 3.33341 3.33268 4.22885 3.33268 5.33341V7.33342H1.99935V5.33341ZM13.9993 8.66675H12.666V10.6667C12.666 11.7713 11.7706 12.6667 10.666 12.6667H8.66602V14.0001H10.666C12.5069 14.0001 13.9993 12.5077 13.9993 10.6667V8.66675Z" fill="#344054"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 2.66659H3.33333V13.3333H12.6667V5.33325H10V2.66659ZM2 1.99445C2 1.62929 2.29833 1.33325 2.66567 1.33325H10.6667L13.9998 4.66658L14 13.9949C14 14.3659 13.7034 14.6666 13.3377 14.6666H2.66227C2.29651 14.6666 2 14.3631 2 14.0054V1.99445ZM11.7713 7.99992L9.4142 10.3569L8.4714 9.41412L9.8856 7.99992L8.4714 6.58571L9.4142 5.6429L11.7713 7.99992ZM4.22877 7.99992L6.58579 5.6429L7.5286 6.58571L6.11438 7.99992L7.5286 9.41412L6.58579 10.3569L4.22877 7.99992Z" fill="#344054"/>
</svg>

Before

Width:  |  Height:  |  Size: 586 B

View File

@@ -1,36 +0,0 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "16",
"height": "16",
"viewBox": "0 0 16 16",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "apps-2-line"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Vector",
"d": "M4.66602 7.6665C3.00916 7.6665 1.66602 6.32336 1.66602 4.6665C1.66602 3.00965 3.00916 1.6665 4.66602 1.6665C6.32287 1.6665 7.66602 3.00965 7.66602 4.6665C7.66602 6.32336 6.32287 7.6665 4.66602 7.6665ZM4.66602 14.3332C3.00916 14.3332 1.66602 12.99 1.66602 11.3332C1.66602 9.6763 3.00916 8.33317 4.66602 8.33317C6.32287 8.33317 7.66602 9.6763 7.66602 11.3332C7.66602 12.99 6.32287 14.3332 4.66602 14.3332ZM11.3327 7.6665C9.67582 7.6665 8.33268 6.32336 8.33268 4.6665C8.33268 3.00965 9.67582 1.6665 11.3327 1.6665C12.9895 1.6665 14.3327 3.00965 14.3327 4.6665C14.3327 6.32336 12.9895 7.6665 11.3327 7.6665ZM11.3327 14.3332C9.67582 14.3332 8.33268 12.99 8.33268 11.3332C8.33268 9.6763 9.67582 8.33317 11.3327 8.33317C12.9895 8.33317 14.3327 9.6763 14.3327 11.3332C14.3327 12.99 12.9895 14.3332 11.3327 14.3332ZM4.66602 6.33317C5.58649 6.33317 6.33268 5.58698 6.33268 4.6665C6.33268 3.74603 5.58649 2.99984 4.66602 2.99984C3.74554 2.99984 2.99935 3.74603 2.99935 4.6665C2.99935 5.58698 3.74554 6.33317 4.66602 6.33317ZM4.66602 12.9998C5.58649 12.9998 6.33268 12.2536 6.33268 11.3332C6.33268 10.4127 5.58649 9.6665 4.66602 9.6665C3.74554 9.6665 2.99935 10.4127 2.99935 11.3332C2.99935 12.2536 3.74554 12.9998 4.66602 12.9998ZM11.3327 6.33317C12.2531 6.33317 12.9993 5.58698 12.9993 4.6665C12.9993 3.74603 12.2531 2.99984 11.3327 2.99984C10.4122 2.99984 9.66602 3.74603 9.66602 4.6665C9.66602 5.58698 10.4122 6.33317 11.3327 6.33317ZM11.3327 12.9998C12.2531 12.9998 12.9993 12.2536 12.9993 11.3332C12.9993 10.4127 12.2531 9.6665 11.3327 9.6665C10.4122 9.6665 9.66602 10.4127 9.66602 11.3332C9.66602 12.2536 10.4122 12.9998 11.3327 12.9998Z",
"fill": "currentColor"
},
"children": []
}
]
}
]
},
"name": "Apps02"
}

View File

@@ -1,20 +0,0 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Apps02.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconData } from '@/app/components/base/icons/IconBase'
const Icon = (
{
ref,
...props
}: React.SVGProps<SVGSVGElement> & {
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
},
) => <IconBase {...props} ref={ref} data={data as IconData} />
Icon.displayName = 'Apps02'
export default Icon

View File

@@ -1,26 +0,0 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "16",
"height": "16",
"viewBox": "0 0 16 16",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M4.66602 14.3334C3.00916 14.3334 1.66602 12.9903 1.66602 11.3334C1.66602 9.67655 3.00916 8.33342 4.66602 8.33342C6.32287 8.33342 7.66602 9.67655 7.66602 11.3334C7.66602 12.9903 6.32287 14.3334 4.66602 14.3334ZM11.3327 7.66675C9.67582 7.66675 8.33268 6.3236 8.33268 4.66675C8.33268 3.00989 9.67582 1.66675 11.3327 1.66675C12.9895 1.66675 14.3327 3.00989 14.3327 4.66675C14.3327 6.3236 12.9895 7.66675 11.3327 7.66675ZM4.66602 13.0001C5.58649 13.0001 6.33268 12.2539 6.33268 11.3334C6.33268 10.4129 5.58649 9.66675 4.66602 9.66675C3.74554 9.66675 2.99935 10.4129 2.99935 11.3334C2.99935 12.2539 3.74554 13.0001 4.66602 13.0001ZM11.3327 6.33342C12.2531 6.33342 12.9993 5.58722 12.9993 4.66675C12.9993 3.74627 12.2531 3.00008 11.3327 3.00008C10.4122 3.00008 9.66602 3.74627 9.66602 4.66675C9.66602 5.58722 10.4122 6.33342 11.3327 6.33342ZM1.99935 5.33341C1.99935 3.49247 3.49174 2.00008 5.33268 2.00008H7.33268V3.33341H5.33268C4.22812 3.33341 3.33268 4.22885 3.33268 5.33341V7.33342H1.99935V5.33341ZM13.9993 8.66675H12.666V10.6667C12.666 11.7713 11.7706 12.6667 10.666 12.6667H8.66602V14.0001H10.666C12.5069 14.0001 13.9993 12.5077 13.9993 10.6667V8.66675Z",
"fill": "currentColor"
},
"children": []
}
]
},
"name": "Exchange02"
}

View File

@@ -1,20 +0,0 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Exchange02.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconData } from '@/app/components/base/icons/IconBase'
const Icon = (
{
ref,
...props
}: React.SVGProps<SVGSVGElement> & {
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
},
) => <IconBase {...props} ref={ref} data={data as IconData} />
Icon.displayName = 'Exchange02'
export default Icon

View File

@@ -1,26 +0,0 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "16",
"height": "16",
"viewBox": "0 0 16 16",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M10 2.66659H3.33333V13.3333H12.6667V5.33325H10V2.66659ZM2 1.99445C2 1.62929 2.29833 1.33325 2.66567 1.33325H10.6667L13.9998 4.66658L14 13.9949C14 14.3659 13.7034 14.6666 13.3377 14.6666H2.66227C2.29651 14.6666 2 14.3631 2 14.0054V1.99445ZM11.7713 7.99992L9.4142 10.3569L8.4714 9.41412L9.8856 7.99992L8.4714 6.58571L9.4142 5.6429L11.7713 7.99992ZM4.22877 7.99992L6.58579 5.6429L7.5286 6.58571L6.11438 7.99992L7.5286 9.41412L6.58579 10.3569L4.22877 7.99992Z",
"fill": "currentColor"
},
"children": []
}
]
},
"name": "FileCode"
}

View File

@@ -1,20 +0,0 @@
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './FileCode.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconData } from '@/app/components/base/icons/IconBase'
const Icon = (
{
ref,
...props
}: React.SVGProps<SVGSVGElement> & {
ref?: React.RefObject<React.RefObject<HTMLOrSVGElement>>;
},
) => <IconBase {...props} ref={ref} data={data as IconData} />
Icon.displayName = 'FileCode'
export default Icon

View File

@@ -1,10 +1,7 @@
export { default as Apps02 } from './Apps02'
export { default as BubbleX } from './BubbleX'
export { default as Colors } from './Colors'
export { default as DragHandle } from './DragHandle'
export { default as Env } from './Env'
export { default as Exchange02 } from './Exchange02'
export { default as FileCode } from './FileCode'
export { default as GlobalVariable } from './GlobalVariable'
export { default as Icon3Dots } from './Icon3Dots'
export { default as LongArrowLeft } from './LongArrowLeft'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,80 +0,0 @@
'use client'
import { useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useMount } from 'ahooks'
import cn from '@/utils/classnames'
import { Apps02 } from '@/app/components/base/icons/src/vender/line/others'
import I18n from '@/context/i18n'
import { getLanguage } from '@/i18n-config/language'
import { useStore as useLabelStore } from '@/app/components/tools/labels/store'
import { fetchLabelList } from '@/service/tools'
import { renderI18nObject } from '@/i18n-config'
type Props = {
value: string
onSelect: (type: string) => void
}
const Icon = ({ svgString, active }: { svgString: string; active: boolean }) => {
const svgRef = useRef<SVGSVGElement | null>(null)
const SVGParser = (svg: string) => {
if (!svg)
return null
const parser = new DOMParser()
const doc = parser.parseFromString(svg, 'image/svg+xml')
return doc.documentElement
}
useMount(() => {
const svgElement = SVGParser(svgString)
if (svgRef.current && svgElement)
svgRef.current.appendChild(svgElement)
})
return <svg className={cn('h-4 w-4 text-gray-700', active && '!text-primary-600')} ref={svgRef} />
}
const Category = ({
value,
onSelect,
}: Props) => {
const { t } = useTranslation()
const { locale } = useContext(I18n)
const language = getLanguage(locale)
const labelList = useLabelStore(s => s.labelList)
const setLabelList = useLabelStore(s => s.setLabelList)
useMount(() => {
fetchLabelList().then((res) => {
setLabelList(res)
})
})
return (
<div className='mb-3'>
<div className='px-3 py-0.5 text-xs font-medium leading-[18px] text-gray-500'>{t('tools.addToolModal.category').toLocaleUpperCase()}</div>
<div className={cn('mb-0.5 flex cursor-pointer items-center rounded-lg p-1 pl-3 text-sm leading-5 text-gray-700 hover:bg-white', value === '' && '!bg-white font-medium !text-primary-600')} onClick={() => onSelect('')}>
<Apps02 className='mr-2 h-4 w-4 shrink-0' />
{t('tools.type.all')}
</div>
{labelList.map((label) => {
const labelText = typeof label.label === 'string'
? label.label
: (label.label ? renderI18nObject(label.label, language) : '')
return (
<div
key={label.name}
title={labelText}
className={cn('mb-0.5 flex cursor-pointer items-center overflow-hidden truncate rounded-lg p-1 pl-3 text-sm leading-5 text-gray-700 hover:bg-white', value === label.name && '!bg-white font-medium !text-primary-600')}
onClick={() => onSelect(label.name)}
>
<div className='mr-2 h-4 w-4 shrink-0'>
<Icon active={value === label.name} svgString={label.icon || ''} />
</div>
{labelText}
</div>
)
})}
</div>
)
}
export default Category

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,258 +0,0 @@
'use client'
import type { FC } from 'react'
import React, { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { produce } from 'immer'
import {
RiAddLine,
RiCloseLine,
} from '@remixicon/react'
import { useMount } from 'ahooks'
import type { Collection, CustomCollectionBackend, Tool } from '../types'
import type { CollectionType } from '../types'
import Type from './type'
import Category from './category'
import Tools from './tools'
import cn from '@/utils/classnames'
import { basePath } from '@/utils/var'
import I18n from '@/context/i18n'
import Drawer from '@/app/components/base/drawer'
import Button from '@/app/components/base/button'
import Loading from '@/app/components/base/loading'
import Input from '@/app/components/base/input'
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
import {
createCustomCollection,
fetchAllBuiltInTools,
fetchAllCustomTools,
fetchAllWorkflowTools,
removeBuiltInToolCredential,
updateBuiltInToolCredential,
} from '@/service/tools'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import Toast from '@/app/components/base/toast'
import ConfigContext from '@/context/debug-configuration'
import type { ModelConfig } from '@/models/debug'
type Props = {
onHide: () => void
}
// Add and Edit
const AddToolModal: FC<Props> = ({
onHide,
}) => {
const { t } = useTranslation()
const { locale } = useContext(I18n)
const [currentType, setCurrentType] = useState('builtin')
const [currentCategory, setCurrentCategory] = useState('')
const [keywords, setKeywords] = useState<string>('')
const handleKeywordsChange = (value: string) => {
setKeywords(value)
}
const isMatchingKeywords = (text: string, keywords: string) => {
return text.toLowerCase().includes(keywords.toLowerCase())
}
const [toolList, setToolList] = useState<ToolWithProvider[]>([])
const [listLoading, setListLoading] = useState(true)
const getAllTools = async () => {
setListLoading(true)
const buildInTools = await fetchAllBuiltInTools()
if (basePath) {
buildInTools.forEach((item) => {
if (typeof item.icon == 'string' && !item.icon.includes(basePath))
item.icon = `${basePath}${item.icon}`
})
}
const customTools = await fetchAllCustomTools()
const workflowTools = await fetchAllWorkflowTools()
const mergedToolList = [
...buildInTools,
...customTools,
...workflowTools.filter((toolWithProvider) => {
return !toolWithProvider.tools.some((tool) => {
return !!tool.parameters.find(item => item.name === '__image')
})
}),
]
setToolList(mergedToolList)
setListLoading(false)
}
const filteredList = useMemo(() => {
return toolList.filter((toolWithProvider) => {
if (currentType === 'all')
return true
else
return toolWithProvider.type === currentType
}).filter((toolWithProvider) => {
if (!currentCategory)
return true
else
return toolWithProvider.labels.includes(currentCategory)
}).filter((toolWithProvider) => {
return (
isMatchingKeywords(toolWithProvider.name, keywords)
|| toolWithProvider.tools.some((tool) => {
return Object.values(tool.label).some((label) => {
return isMatchingKeywords(label, keywords)
})
})
)
})
}, [currentType, currentCategory, toolList, keywords])
const {
modelConfig,
setModelConfig,
} = useContext(ConfigContext)
const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => {
await createCustomCollection(data)
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),
})
setIsShowEditCustomCollectionModal(false)
getAllTools()
}
const [showSettingAuth, setShowSettingAuth] = useState(false)
const [collection, setCollection] = useState<Collection>()
const toolSelectHandle = (collection: Collection, tool: Tool) => {
const parameters: Record<string, string> = {}
if (tool.parameters) {
tool.parameters.forEach((item) => {
parameters[item.name] = ''
})
}
const nexModelConfig = produce(modelConfig, (draft: ModelConfig) => {
draft.agentConfig.tools.push({
provider_id: collection.id || collection.name,
provider_type: collection.type as CollectionType,
provider_name: collection.name,
tool_name: tool.name,
tool_label: tool.label[locale] || tool.label[locale.replaceAll('-', '_')],
tool_parameters: parameters,
enabled: true,
})
})
setModelConfig(nexModelConfig)
}
const authSelectHandle = (provider: Collection) => {
setCollection(provider)
setShowSettingAuth(true)
}
const updateBuiltinAuth = async (value: Record<string, any>) => {
if (!collection)
return
await updateBuiltInToolCredential(collection.name, value)
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),
})
await getAllTools()
setShowSettingAuth(false)
}
const removeBuiltinAuth = async () => {
if (!collection)
return
await removeBuiltInToolCredential(collection.name)
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),
})
await getAllTools()
setShowSettingAuth(false)
}
useMount(() => {
getAllTools()
})
return (
<>
<Drawer
isOpen
mask
clickOutsideNotOpen
onClose={onHide}
footer={null}
panelClassName={cn('mx-2 mb-3 mt-16 rounded-xl !p-0 sm:mr-2', 'mt-2 !w-[640px]', '!max-w-[640px]')}
>
<div
className='flex w-full rounded-xl border-[0.5px] border-gray-200 bg-white shadow-xl'
style={{
height: 'calc(100vh - 16px)',
}}
>
<div className='relative w-[200px] shrink-0 overflow-y-auto rounded-l-xl border-r-[0.5px] border-black/2 bg-gray-100 pb-3'>
<div className='sticky left-0 right-0 top-0'>
<div className='text-md sticky left-0 right-0 top-0 px-5 py-3 font-semibold text-gray-900'>{t('tools.addTool')}</div>
<div className='px-3 pb-4 pt-2'>
<Button variant='primary' className='w-[176px]' onClick={() => setIsShowEditCustomCollectionModal(true)}>
<RiAddLine className='mr-1 h-4 w-4' />
{t('tools.createCustomTool')}
</Button>
</div>
</div>
<div className='px-2 py-1'>
<Type value={currentType} onSelect={setCurrentType} />
<Category value={currentCategory} onSelect={setCurrentCategory} />
</div>
</div>
<div className='relative grow overflow-y-auto rounded-r-xl bg-white'>
<div className='sticky left-0 right-0 top-0 z-10 flex items-center gap-1 bg-white p-2'>
<div className='grow'>
<Input
showLeftIcon
showClearIcon
value={keywords}
onChange={e => handleKeywordsChange(e.target.value)}
onClear={() => handleKeywordsChange('')}
/>
</div>
<div className='ml-2 mr-1 h-4 w-[1px] bg-gray-200'></div>
<div className='cursor-pointer p-2' onClick={onHide}>
<RiCloseLine className='h-4 w-4 text-gray-500' />
</div>
</div>
{listLoading && (
<div className='flex h-[200px] items-center justify-center bg-white'>
<Loading />
</div>
)}
{!listLoading && (
<Tools
showWorkflowEmpty={currentType === 'workflow'}
tools={filteredList}
addedTools={(modelConfig?.agentConfig?.tools as any) || []}
onSelect={toolSelectHandle}
onAuthSetup={authSelectHandle}
/>
)}
</div>
</div>
</Drawer>
{isShowEditCollectionToolModal && (
<EditCustomToolModal
positionLeft
payload={null}
onHide={() => setIsShowEditCustomCollectionModal(false)}
onAdd={doCreateCustomToolCollection}
/>
)}
{showSettingAuth && collection && (
<ConfigCredential
collection={collection}
onCancel={() => setShowSettingAuth(false)}
onSaved={updateBuiltinAuth}
onRemove={removeBuiltinAuth}
/>
)}
</>
)
}
export default React.memo(AddToolModal)

View File

@@ -1,158 +0,0 @@
import {
memo,
useCallback,
} from 'react'
import { basePath } from '@/utils/var'
import { useTranslation } from 'react-i18next'
import {
RiAddLine,
} from '@remixicon/react'
import cn from '@/utils/classnames'
import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
import { Check } from '@/app/components/base/icons/src/vender/line/general'
import { Tag01 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import { BlockEnum } from '@/app/components/workflow/types'
import BlockIcon from '@/app/components/workflow/block-icon'
import Tooltip from '@/app/components/base/tooltip'
import Button from '@/app/components/base/button'
import { useGetLanguage } from '@/context/i18n'
import { useStore as useLabelStore } from '@/app/components/tools/labels/store'
import Empty from '@/app/components/tools/add-tool-modal/empty'
import type { Tool } from '@/app/components/tools/types'
import { CollectionType } from '@/app/components/tools/types'
import type { AgentTool } from '@/types/app'
import { MAX_TOOLS_NUM } from '@/config'
import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { renderI18nObject } from '@/i18n-config'
const resolveI18nText = (value: TypeWithI18N | string | undefined, language: string): string => {
if (!value)
return ''
return typeof value === 'string' ? value : renderI18nObject(value, language)
}
type ToolsProps = {
showWorkflowEmpty: boolean
tools: ToolWithProvider[]
addedTools: AgentTool[]
onSelect: (provider: ToolWithProvider, tool: Tool) => void
onAuthSetup: (provider: ToolWithProvider) => void
}
const Blocks = ({
showWorkflowEmpty,
tools,
addedTools,
onSelect,
onAuthSetup,
}: ToolsProps) => {
const { t } = useTranslation()
const language = useGetLanguage()
const labelList = useLabelStore(s => s.labelList)
const addable = addedTools.length < MAX_TOOLS_NUM
const renderGroup = useCallback((toolWithProvider: ToolWithProvider) => {
const list = toolWithProvider.tools
const needAuth = toolWithProvider.allow_delete && !toolWithProvider.is_team_authorization && toolWithProvider.type === CollectionType.builtIn
return (
<div
key={toolWithProvider.id}
className='group mb-1 last-of-type:mb-0'
>
<div className='flex h-[22px] w-full items-center justify-between pl-3 pr-1 text-xs font-medium text-gray-500'>
{resolveI18nText(toolWithProvider.label, language)}
<a className='hidden cursor-pointer items-center group-hover:flex' href={`${basePath}/tools?category=${toolWithProvider.type}`} target='_blank'>{t('tools.addToolModal.manageInTools')}<ArrowUpRight className='ml-0.5 h-3 w-3' /></a>
</div>
{list.map((tool) => {
const labelContent = (() => {
if (!tool.labels)
return ''
return tool.labels.map((name) => {
const label = labelList.find(item => item.name === name)
return resolveI18nText(label?.label, language)
}).filter(Boolean).join(', ')
})()
const added = !!addedTools?.find(v => v.provider_id === toolWithProvider.id && v.provider_type === toolWithProvider.type && v.tool_name === tool.name)
return (
<Tooltip
key={tool.name}
position='bottom'
popupClassName='!p-0 !px-3 !py-2.5 !w-[210px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !bg-transparent !rounded-xl !shadow-lg translate-x-[108px]'
popupContent={(
<div>
<BlockIcon
size='md'
className='mb-2'
type={BlockEnum.Tool}
toolIcon={toolWithProvider.icon}
/>
<div className='mb-1 text-sm leading-5 text-gray-900'>{resolveI18nText(tool.label, language)}</div>
<div className='text-xs leading-[18px] text-gray-700'>{resolveI18nText(tool.description, language)}</div>
{tool.labels?.length > 0 && (
<div className='mt-1 flex shrink-0 items-center'>
<div className='relative flex w-full items-center gap-1 rounded-md py-1 text-gray-500' title={labelContent}>
<Tag01 className='h-3 w-3 shrink-0 text-gray-500' />
<div className='grow truncate text-start text-xs font-normal leading-[18px]'>{labelContent}</div>
</div>
</div>
)}
</div>
)}
>
<div className='group/item flex h-8 w-full cursor-pointer items-center rounded-lg pl-3 pr-1 hover:bg-gray-50'>
<BlockIcon
className={cn('mr-2 shrink-0', needAuth && 'opacity-30')}
type={BlockEnum.Tool}
toolIcon={toolWithProvider.icon}
/>
<div className={cn('grow truncate text-sm text-gray-900', needAuth && 'opacity-30')}>{resolveI18nText(tool.label, language)}</div>
{!needAuth && added && (
<div className='flex items-center gap-1 rounded-[6px] border border-gray-100 bg-white px-2 py-[3px] text-xs font-medium leading-[18px] text-gray-300'>
<Check className='h-3 w-3' />
{t('tools.addToolModal.added').toLocaleUpperCase()}
</div>
)}
{!needAuth && !added && addable && (
<Button
variant='secondary-accent'
size='small'
className={cn('hidden shrink-0 items-center group-hover/item:flex')}
onClick={() => onSelect(toolWithProvider, tool)}
>
<RiAddLine className='h-3 w-3' />
{t('tools.addToolModal.add').toLocaleUpperCase()}
</Button>
)}
{needAuth && (
<Button
variant='secondary-accent'
size='small'
className={cn('hidden shrink-0 group-hover/item:flex')}
onClick={() => onAuthSetup(toolWithProvider)}
>{t('tools.auth.setup')}</Button>
)}
</div>
</Tooltip>
)
})}
</div>
)
}, [addable, language, t, labelList, addedTools, onAuthSetup, onSelect])
return (
<div className='max-w-[440px] p-1 pb-6'>
{!tools.length && !showWorkflowEmpty && (
<div className='flex h-[22px] items-center px-3 text-xs font-medium text-gray-500'>{t('workflow.tabs.noResult')}</div>
)}
{!tools.length && showWorkflowEmpty && (
<div className='pt-[280px]'>
<Empty />
</div>
)}
{!!tools.length && tools.map(renderGroup)}
</div>
)
}
export default memo(Blocks)

View File

@@ -1,34 +0,0 @@
'use client'
import { useTranslation } from 'react-i18next'
import cn from '@/utils/classnames'
import { Exchange02, FileCode } from '@/app/components/base/icons/src/vender/line/others'
type Props = {
value: string
onSelect: (type: string) => void
}
const Types = ({
value,
onSelect,
}: Props) => {
const { t } = useTranslation()
return (
<div className='mb-3'>
<div className={cn('mb-0.5 flex cursor-pointer items-center rounded-lg p-1 pl-3 text-sm leading-5 hover:bg-white', value === 'builtin' && '!bg-white font-medium')} onClick={() => onSelect('builtin')}>
<div className="mr-2 h-4 w-4 shrink-0 bg-[url('~@/app/components/tools/add-tool-modal/D.png')] bg-cover bg-no-repeat" />
<span className={cn('text-gray-700', value === 'builtin' && '!text-primary-600')}>{t('tools.type.builtIn')}</span>
</div>
<div className={cn('mb-0.5 flex cursor-pointer items-center rounded-lg p-1 pl-3 text-sm leading-5 text-gray-700 hover:bg-white', value === 'api' && '!bg-white font-medium !text-primary-600')} onClick={() => onSelect('api')}>
<FileCode className='mr-2 h-4 w-4 shrink-0' />
{t('tools.type.custom')}
</div>
<div className={cn('mb-0.5 flex cursor-pointer items-center rounded-lg p-1 pl-3 text-sm leading-5 text-gray-700 hover:bg-white', value === 'workflow' && '!bg-white font-medium !text-primary-600')} onClick={() => onSelect('workflow')}>
<Exchange02 className='mr-2 h-4 w-4 shrink-0' />
{t('tools.type.workflow')}
</div>
</div>
)
}
export default Types

View File

@@ -11,7 +11,7 @@ import Input from '@/app/components/base/input'
import ProviderDetail from '@/app/components/tools/provider/detail'
import Empty from '@/app/components/plugins/marketplace/empty'
import CustomCreateCard from '@/app/components/tools/provider/custom-create-card'
import WorkflowToolEmpty from '@/app/components/tools/add-tool-modal/empty'
import WorkflowToolEmpty from '@/app/components/tools/provider/empty'
import Card from '@/app/components/plugins/card'
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'

View File

@@ -4,7 +4,7 @@ import IndexBar, { groupItems } from './index-bar'
import type { ToolDefaultValue, ToolValue } from './types'
import type { ToolTypeEnum } from './types'
import { ViewType } from './view-type-select'
import Empty from '@/app/components/tools/add-tool-modal/empty'
import Empty from '@/app/components/tools/provider/empty'
import { useGetLanguage } from '@/context/i18n'
import ToolListTreeView from './tool/tool-list-tree-view/list'
import ToolListFlatView from './tool/tool-list-flat-view/list'