feat: the frontend part of mcp (#22131)
Co-authored-by: jZonG <jzongcode@gmail.com> Co-authored-by: Novice <novice12185727@gmail.com> Co-authored-by: nite-knite <nkCoding@gmail.com> Co-authored-by: Hanqing Zhao <sherry9277@gmail.com>
This commit is contained in:
@@ -68,6 +68,7 @@ function formatStrategy(input: StrategyPluginDetail[], getIcon: (i: string) => s
|
||||
icon: getIcon(item.declaration.identity.icon),
|
||||
label: item.declaration.identity.label as any,
|
||||
type: CollectionType.all,
|
||||
meta: item.meta,
|
||||
tools: item.declaration.strategies.map(strategy => ({
|
||||
name: strategy.identity.name,
|
||||
author: strategy.identity.author,
|
||||
@@ -89,10 +90,13 @@ function formatStrategy(input: StrategyPluginDetail[], getIcon: (i: string) => s
|
||||
export type AgentStrategySelectorProps = {
|
||||
value?: Strategy,
|
||||
onChange: (value?: Strategy) => void,
|
||||
canChooseMCPTool: boolean,
|
||||
}
|
||||
|
||||
export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => {
|
||||
const { value, onChange } = props
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
|
||||
const { value, onChange, canChooseMCPTool } = props
|
||||
const [open, setOpen] = useState(false)
|
||||
const [viewType, setViewType] = useState<ViewType>(ViewType.flat)
|
||||
const [query, setQuery] = useState('')
|
||||
@@ -132,8 +136,6 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
|
||||
plugins: notInstalledPlugins = [],
|
||||
} = useMarketplacePlugins()
|
||||
|
||||
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
|
||||
|
||||
useEffect(() => {
|
||||
if (!enable_marketplace) return
|
||||
if (query) {
|
||||
@@ -214,21 +216,25 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
|
||||
agent_strategy_label: tool!.tool_label,
|
||||
agent_output_schema: tool!.output_schema,
|
||||
plugin_unique_identifier: tool!.provider_id,
|
||||
meta: tool!.meta,
|
||||
})
|
||||
setOpen(false)
|
||||
}}
|
||||
className='h-full max-h-full max-w-none overflow-y-auto'
|
||||
indexBarClassName='top-0 xl:top-36' showWorkflowEmpty={false} hasSearchText={false} />
|
||||
{enable_marketplace
|
||||
&& <PluginList
|
||||
indexBarClassName='top-0 xl:top-36'
|
||||
hasSearchText={false}
|
||||
canNotSelectMultiple
|
||||
canChooseMCPTool={canChooseMCPTool}
|
||||
isAgent
|
||||
/>
|
||||
{enable_marketplace && <PluginList
|
||||
ref={pluginRef}
|
||||
wrapElemRef={wrapElemRef}
|
||||
list={notInstalledPlugins}
|
||||
searchText={query}
|
||||
tags={DEFAULT_TAGS}
|
||||
disableMaxWidth
|
||||
/>
|
||||
}
|
||||
/>}
|
||||
</main>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
|
@@ -19,6 +19,8 @@ import { useWorkflowStore } from '../../../store'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import type { NodeOutPutVar } from '../../../types'
|
||||
import type { Node } from 'reactflow'
|
||||
import type { PluginMeta } from '@/app/components/plugins/types'
|
||||
import { noop } from 'lodash'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
export type Strategy = {
|
||||
@@ -27,6 +29,7 @@ export type Strategy = {
|
||||
agent_strategy_label: string
|
||||
agent_output_schema: Record<string, any>
|
||||
plugin_unique_identifier: string
|
||||
meta?: PluginMeta
|
||||
}
|
||||
|
||||
export type AgentStrategyProps = {
|
||||
@@ -38,6 +41,7 @@ export type AgentStrategyProps = {
|
||||
nodeOutputVars?: NodeOutPutVar[],
|
||||
availableNodes?: Node[],
|
||||
nodeId?: string
|
||||
canChooseMCPTool: boolean
|
||||
}
|
||||
|
||||
type CustomSchema<Type, Field = {}> = Omit<CredentialFormSchema, 'type'> & { type: Type } & Field
|
||||
@@ -48,7 +52,7 @@ type MultipleToolSelectorSchema = CustomSchema<'array[tools]'>
|
||||
type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema
|
||||
|
||||
export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId } = props
|
||||
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId, canChooseMCPTool } = props
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration)
|
||||
@@ -57,6 +61,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
const {
|
||||
setControlPromptEditorRerenderKey,
|
||||
} = workflowStore.getState()
|
||||
|
||||
const override: ComponentProps<typeof Form<CustomField>>['override'] = [
|
||||
[FormTypeEnum.textNumber, FormTypeEnum.textInput],
|
||||
(schema, props) => {
|
||||
@@ -168,6 +173,8 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
value={value}
|
||||
onSelect={item => onChange(item)}
|
||||
onDelete={() => onChange(null)}
|
||||
canChooseMCPTool={canChooseMCPTool}
|
||||
onSelectMultiple={noop}
|
||||
/>
|
||||
</Field>
|
||||
)
|
||||
@@ -189,13 +196,14 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
onChange={onChange}
|
||||
supportCollapse
|
||||
required={schema.required}
|
||||
canChooseMCPTool={canChooseMCPTool}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return <div className='space-y-2'>
|
||||
<AgentStrategySelector value={strategy} onChange={onStrategyChange} />
|
||||
<AgentStrategySelector value={strategy} onChange={onStrategyChange} canChooseMCPTool={canChooseMCPTool} />
|
||||
{
|
||||
strategy
|
||||
? <div>
|
||||
@@ -215,6 +223,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
|
||||
nodeId={nodeId}
|
||||
nodeOutputVars={nodeOutputVars || []}
|
||||
availableNodes={availableNodes || []}
|
||||
canChooseMCPTool={canChooseMCPTool}
|
||||
/>
|
||||
</div>
|
||||
: <ListEmpty
|
||||
|
@@ -76,7 +76,7 @@ const Base: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Wrap className={cn(wrapClassName)} style={wrapStyle} isInNode={isInNode} isExpand={isExpand}>
|
||||
<div ref={ref} className={cn(className, isExpand && 'h-full', 'rounded-lg border', isFocus ? 'border-transparent bg-components-input-bg-normal' : 'overflow-hidden border-components-input-border-hover bg-components-input-bg-hover')}>
|
||||
<div ref={ref} className={cn(className, isExpand && 'h-full', 'rounded-lg border', !isFocus ? 'border-transparent bg-components-input-bg-normal' : 'overflow-hidden border-components-input-border-hover bg-components-input-bg-hover')}>
|
||||
<div className='flex h-7 items-center justify-between pl-3 pr-2 pt-1'>
|
||||
<div className='system-xs-semibold-uppercase text-text-secondary'>{title}</div>
|
||||
<div className='flex items-center' onClick={(e) => {
|
||||
|
@@ -23,7 +23,7 @@ export type Props = {
|
||||
value?: string | object
|
||||
placeholder?: React.JSX.Element | string
|
||||
onChange?: (value: string) => void
|
||||
title?: React.JSX.Element
|
||||
title?: string | React.JSX.Element
|
||||
language: CodeLanguage
|
||||
headerRight?: React.JSX.Element
|
||||
readOnly?: boolean
|
||||
|
@@ -0,0 +1,35 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
value: boolean
|
||||
onChange: (value: boolean) => void
|
||||
}
|
||||
|
||||
const FormInputBoolean: FC<Props> = ({
|
||||
value,
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<div className='flex w-full space-x-1'>
|
||||
<div
|
||||
className={cn(
|
||||
'system-sm-regular flex h-8 grow cursor-default items-center justify-center rounded-md border border-components-option-card-option-border bg-components-option-card-option-bg px-2 text-text-secondary',
|
||||
!value && 'cursor-pointer hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs',
|
||||
value && 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs',
|
||||
)}
|
||||
onClick={() => onChange(true)}
|
||||
>True</div>
|
||||
<div
|
||||
className={cn(
|
||||
'system-sm-regular flex h-8 grow cursor-default items-center justify-center rounded-md border border-components-option-card-option-border bg-components-option-card-option-bg px-2 text-text-secondary',
|
||||
value && 'cursor-pointer hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs',
|
||||
!value && 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs',
|
||||
)}
|
||||
onClick={() => onChange(false)}
|
||||
>False</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default FormInputBoolean
|
@@ -0,0 +1,279 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { ToolVarInputs } from '@/app/components/workflow/nodes/tool/types'
|
||||
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||
import { VarType } from '@/app/components/workflow/types'
|
||||
|
||||
import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import FormInputTypeSwitch from './form-input-type-switch'
|
||||
import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import MixedVariableTextInput from '@/app/components/workflow/nodes/tool/components/mixed-variable-text-input'
|
||||
import FormInputBoolean from './form-input-boolean'
|
||||
import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector'
|
||||
import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector'
|
||||
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { Tool } from '@/app/components/tools/types'
|
||||
|
||||
type Props = {
|
||||
readOnly: boolean
|
||||
nodeId: string
|
||||
schema: CredentialFormSchema
|
||||
value: ToolVarInputs
|
||||
onChange: (value: any) => void
|
||||
inPanel?: boolean
|
||||
currentTool?: Tool
|
||||
currentProvider?: ToolWithProvider
|
||||
}
|
||||
|
||||
const FormInputItem: FC<Props> = ({
|
||||
readOnly,
|
||||
nodeId,
|
||||
schema,
|
||||
value,
|
||||
onChange,
|
||||
inPanel,
|
||||
currentTool,
|
||||
currentProvider,
|
||||
}) => {
|
||||
const language = useLanguage()
|
||||
|
||||
const {
|
||||
placeholder,
|
||||
variable,
|
||||
type,
|
||||
default: defaultValue,
|
||||
options,
|
||||
scope,
|
||||
} = schema as any
|
||||
const varInput = value[variable]
|
||||
const isString = type === FormTypeEnum.textInput || type === FormTypeEnum.secretInput
|
||||
const isNumber = type === FormTypeEnum.textNumber
|
||||
const isObject = type === FormTypeEnum.object
|
||||
const isArray = type === FormTypeEnum.array
|
||||
const isShowJSONEditor = isObject || isArray
|
||||
const isFile = type === FormTypeEnum.file || type === FormTypeEnum.files
|
||||
const isBoolean = type === FormTypeEnum.boolean
|
||||
const isSelect = type === FormTypeEnum.select || type === FormTypeEnum.dynamicSelect
|
||||
const isAppSelector = type === FormTypeEnum.appSelector
|
||||
const isModelSelector = type === FormTypeEnum.modelSelector
|
||||
const showTypeSwitch = isNumber || isObject || isArray
|
||||
const isConstant = varInput?.type === VarKindType.constant || !varInput?.type
|
||||
const showVariableSelector = isFile || varInput?.type === VarKindType.variable
|
||||
|
||||
const { availableVars, availableNodesWithParent } = useAvailableVarList(nodeId, {
|
||||
onlyLeafNodeVar: false,
|
||||
filterVar: (varPayload: Var) => {
|
||||
return [VarType.string, VarType.number, VarType.secret].includes(varPayload.type)
|
||||
},
|
||||
})
|
||||
|
||||
const targetVarType = () => {
|
||||
if (isString)
|
||||
return VarType.string
|
||||
else if (isNumber)
|
||||
return VarType.number
|
||||
else if (type === FormTypeEnum.files)
|
||||
return VarType.arrayFile
|
||||
else if (type === FormTypeEnum.file)
|
||||
return VarType.file
|
||||
// else if (isSelect)
|
||||
// return VarType.select
|
||||
// else if (isAppSelector)
|
||||
// return VarType.appSelector
|
||||
// else if (isModelSelector)
|
||||
// return VarType.modelSelector
|
||||
// else if (isBoolean)
|
||||
// return VarType.boolean
|
||||
else if (isObject)
|
||||
return VarType.object
|
||||
else if (isArray)
|
||||
return VarType.arrayObject
|
||||
else
|
||||
return VarType.string
|
||||
}
|
||||
|
||||
const getFilterVar = () => {
|
||||
if (isNumber)
|
||||
return (varPayload: any) => varPayload.type === VarType.number
|
||||
else if (isString)
|
||||
return (varPayload: any) => [VarType.string, VarType.number, VarType.secret].includes(varPayload.type)
|
||||
else if (isFile)
|
||||
return (varPayload: any) => [VarType.file, VarType.arrayFile].includes(varPayload.type)
|
||||
else if (isBoolean)
|
||||
return (varPayload: any) => varPayload.type === VarType.boolean
|
||||
else if (isObject)
|
||||
return (varPayload: any) => varPayload.type === VarType.object
|
||||
else if (isArray)
|
||||
return (varPayload: any) => [VarType.array, VarType.arrayString, VarType.arrayNumber, VarType.arrayObject].includes(varPayload.type)
|
||||
return undefined
|
||||
}
|
||||
|
||||
const getVarKindType = () => {
|
||||
if (isFile)
|
||||
return VarKindType.variable
|
||||
if (isSelect || isBoolean || isNumber || isArray || isObject)
|
||||
return VarKindType.constant
|
||||
if (isString)
|
||||
return VarKindType.mixed
|
||||
}
|
||||
|
||||
const handleTypeChange = (newType: string) => {
|
||||
if (newType === VarKindType.variable) {
|
||||
onChange({
|
||||
...value,
|
||||
[variable]: {
|
||||
...varInput,
|
||||
type: VarKindType.variable,
|
||||
value: '',
|
||||
},
|
||||
})
|
||||
}
|
||||
else {
|
||||
onChange({
|
||||
...value,
|
||||
[variable]: {
|
||||
...varInput,
|
||||
type: VarKindType.constant,
|
||||
value: defaultValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleValueChange = (newValue: any) => {
|
||||
onChange({
|
||||
...value,
|
||||
[variable]: {
|
||||
...varInput,
|
||||
type: getVarKindType(),
|
||||
value: isNumber ? Number.parseFloat(newValue) : newValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const handleAppOrModelSelect = (newValue: any) => {
|
||||
onChange({
|
||||
...value,
|
||||
[variable]: {
|
||||
...varInput,
|
||||
...newValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const handleVariableSelectorChange = (newValue: ValueSelector | string, variable: string) => {
|
||||
onChange({
|
||||
...value,
|
||||
[variable]: {
|
||||
...varInput,
|
||||
type: VarKindType.variable,
|
||||
value: newValue || '',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('gap-1', !(isShowJSONEditor && isConstant) && 'flex')}>
|
||||
{showTypeSwitch && (
|
||||
<FormInputTypeSwitch value={varInput?.type || VarKindType.constant} onChange={handleTypeChange}/>
|
||||
)}
|
||||
{isString && (
|
||||
<MixedVariableTextInput
|
||||
readOnly={readOnly}
|
||||
value={varInput?.value as string || ''}
|
||||
onChange={handleValueChange}
|
||||
nodesOutputVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
/>
|
||||
)}
|
||||
{isNumber && isConstant && (
|
||||
<Input
|
||||
className='h-8 grow'
|
||||
type='number'
|
||||
value={varInput?.value || ''}
|
||||
onChange={e => handleValueChange(e.target.value)}
|
||||
placeholder={placeholder?.[language] || placeholder?.en_US}
|
||||
/>
|
||||
)}
|
||||
{isBoolean && (
|
||||
<FormInputBoolean
|
||||
value={varInput?.value as boolean}
|
||||
onChange={handleValueChange}
|
||||
/>
|
||||
)}
|
||||
{isSelect && (
|
||||
<SimpleSelect
|
||||
wrapperClassName='h-8 grow'
|
||||
disabled={readOnly}
|
||||
defaultValue={varInput?.value}
|
||||
items={options.filter((option: { show_on: any[] }) => {
|
||||
if (option.show_on.length)
|
||||
return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value)
|
||||
|
||||
return true
|
||||
}).map((option: { value: any; label: { [x: string]: any; en_US: any } }) => ({ value: option.value, name: option.label[language] || option.label.en_US }))}
|
||||
onSelect={item => handleValueChange(item.value as string)}
|
||||
placeholder={placeholder?.[language] || placeholder?.en_US}
|
||||
/>
|
||||
)}
|
||||
{isShowJSONEditor && isConstant && (
|
||||
<div className='mt-1 w-full'>
|
||||
<CodeEditor
|
||||
title='JSON'
|
||||
value={varInput?.value as any}
|
||||
isExpand
|
||||
isInNode
|
||||
language={CodeLanguage.json}
|
||||
onChange={handleValueChange}
|
||||
className='w-full'
|
||||
placeholder={<div className='whitespace-pre'>{placeholder?.[language] || placeholder?.en_US}</div>}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isAppSelector && (
|
||||
<AppSelector
|
||||
disabled={readOnly}
|
||||
scope={scope || 'all'}
|
||||
value={varInput?.value as any}
|
||||
onSelect={handleAppOrModelSelect}
|
||||
/>
|
||||
)}
|
||||
{isModelSelector && isConstant && (
|
||||
<ModelParameterModal
|
||||
popupClassName='!w-[387px]'
|
||||
isAdvancedMode
|
||||
isInWorkflow
|
||||
value={varInput?.value as any}
|
||||
setModel={handleAppOrModelSelect}
|
||||
readonly={readOnly}
|
||||
scope={scope}
|
||||
/>
|
||||
)}
|
||||
{showVariableSelector && (
|
||||
<VarReferencePicker
|
||||
zIndex={inPanel ? 1000 : undefined}
|
||||
className='h-8 grow'
|
||||
readonly={readOnly}
|
||||
isShowNodeName
|
||||
nodeId={nodeId}
|
||||
value={varInput?.value || []}
|
||||
onChange={value => handleVariableSelectorChange(value, variable)}
|
||||
filterVar={getFilterVar()}
|
||||
schema={schema}
|
||||
valueTypePlaceHolder={targetVarType()}
|
||||
currentTool={currentTool}
|
||||
currentProvider={currentProvider}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default FormInputItem
|
@@ -0,0 +1,47 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiEditLine,
|
||||
} from '@remixicon/react'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { VarType } from '@/app/components/workflow/nodes/tool/types'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
value: VarType
|
||||
onChange: (value: VarType) => void
|
||||
}
|
||||
|
||||
const FormInputTypeSwitch: FC<Props> = ({
|
||||
value,
|
||||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className='inline-flex h-8 shrink-0 gap-px rounded-[10px] bg-components-segmented-control-bg-normal p-0.5'>
|
||||
<Tooltip
|
||||
popupContent={value === VarType.variable ? '' : t('workflow.nodes.common.typeSwitch.variable')}
|
||||
>
|
||||
<div
|
||||
className={cn('cursor-pointer rounded-lg px-2.5 py-1.5 text-text-tertiary hover:bg-state-base-hover', value === VarType.variable && 'bg-components-segmented-control-item-active-bg text-text-secondary shadow-xs hover:bg-components-segmented-control-item-active-bg')}
|
||||
onClick={() => onChange(VarType.variable)}
|
||||
>
|
||||
<Variable02 className='h-4 w-4' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
popupContent={value === VarType.constant ? '' : t('workflow.nodes.common.typeSwitch.input')}
|
||||
>
|
||||
<div
|
||||
className={cn('cursor-pointer rounded-lg px-2.5 py-1.5 text-text-tertiary hover:bg-state-base-hover', value === VarType.constant && 'bg-components-segmented-control-item-active-bg text-text-secondary shadow-xs hover:bg-components-segmented-control-item-active-bg')}
|
||||
onClick={() => onChange(VarType.constant)}
|
||||
>
|
||||
<RiEditLine className='h-4 w-4' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default FormInputTypeSwitch
|
@@ -0,0 +1,22 @@
|
||||
'use client'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { RiAlertFill } from '@remixicon/react'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const McpToolNotSupportTooltip: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='w-[256px]'>
|
||||
{t('plugin.detailPanel.toolSelector.unsupportedMCPTool')}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<RiAlertFill className='size-4 text-text-warning-secondary' />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
export default React.memo(McpToolNotSupportTooltip)
|
@@ -13,7 +13,7 @@ export const SettingItem = memo(({ label, children, status, tooltip }: SettingIt
|
||||
const indicator: ComponentProps<typeof Indicator>['color'] = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined
|
||||
const needTooltip = ['error', 'warning'].includes(status as any)
|
||||
return <div className='relative flex items-center justify-between space-x-1 rounded-md bg-workflow-block-parma-bg px-1.5 py-1 text-xs font-normal'>
|
||||
<div className={classNames('shrink-0 truncate text-text-tertiary system-xs-medium-uppercase', !!children && 'max-w-[100px]')}>
|
||||
<div className={classNames('system-xs-medium-uppercase max-w-full shrink-0 truncate text-text-tertiary', !!children && 'max-w-[100px]')}>
|
||||
{label}
|
||||
</div>
|
||||
<Tooltip popupContent={tooltip} disabled={!needTooltip}>
|
||||
|
@@ -528,6 +528,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
onChange={handleVarReferenceChange}
|
||||
itemWidth={isAddBtnTrigger ? 260 : (minWidth || triggerWidth)}
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
zIndex={zIndex}
|
||||
/>
|
||||
)}
|
||||
</PortalToFollowElemContent>
|
||||
|
@@ -13,6 +13,7 @@ type Props = {
|
||||
onChange: (value: ValueSelector, varDetail: Var) => void
|
||||
itemWidth?: number
|
||||
isSupportFileVar?: boolean
|
||||
zIndex?: number
|
||||
}
|
||||
const VarReferencePopup: FC<Props> = ({
|
||||
vars,
|
||||
@@ -20,6 +21,7 @@ const VarReferencePopup: FC<Props> = ({
|
||||
onChange,
|
||||
itemWidth,
|
||||
isSupportFileVar = true,
|
||||
zIndex,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
@@ -60,6 +62,7 @@ const VarReferencePopup: FC<Props> = ({
|
||||
onChange={onChange}
|
||||
itemWidth={itemWidth}
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
zIndex={zIndex}
|
||||
/>
|
||||
}
|
||||
</div >
|
||||
|
@@ -46,6 +46,7 @@ type ItemProps = {
|
||||
isSupportFileVar?: boolean
|
||||
isException?: boolean
|
||||
isLoopVar?: boolean
|
||||
zIndex?: number
|
||||
}
|
||||
|
||||
const objVarTypes = [VarType.object, VarType.file]
|
||||
@@ -60,6 +61,7 @@ const Item: FC<ItemProps> = ({
|
||||
isSupportFileVar,
|
||||
isException,
|
||||
isLoopVar,
|
||||
zIndex,
|
||||
}) => {
|
||||
const isStructureOutput = itemData.type === VarType.object && (itemData.children as StructuredOutput)?.schema?.properties
|
||||
const isFile = itemData.type === VarType.file && !isStructureOutput
|
||||
@@ -171,7 +173,7 @@ const Item: FC<ItemProps> = ({
|
||||
</div >
|
||||
</PortalToFollowElemTrigger >
|
||||
<PortalToFollowElemContent style={{
|
||||
zIndex: 100,
|
||||
zIndex: zIndex || 100,
|
||||
}}>
|
||||
{(isStructureOutput || isObj) && (
|
||||
<PickerStructurePanel
|
||||
@@ -260,6 +262,7 @@ type Props = {
|
||||
maxHeightClass?: string
|
||||
onClose?: () => void
|
||||
onBlur?: () => void
|
||||
zIndex?: number
|
||||
autoFocus?: boolean
|
||||
}
|
||||
const VarReferenceVars: FC<Props> = ({
|
||||
@@ -272,6 +275,7 @@ const VarReferenceVars: FC<Props> = ({
|
||||
maxHeightClass,
|
||||
onClose,
|
||||
onBlur,
|
||||
zIndex,
|
||||
autoFocus = true,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
@@ -357,6 +361,7 @@ const VarReferenceVars: FC<Props> = ({
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
isException={v.isException}
|
||||
isLoopVar={item.isLoop}
|
||||
zIndex={zIndex}
|
||||
/>
|
||||
))}
|
||||
</div>))
|
||||
|
@@ -32,6 +32,7 @@ import {
|
||||
import { useNodeIterationInteractions } from '../iteration/use-interactions'
|
||||
import { useNodeLoopInteractions } from '../loop/use-interactions'
|
||||
import type { IterationNodeType } from '../iteration/types'
|
||||
import CopyID from '../tool/components/copy-id'
|
||||
import {
|
||||
NodeSourceHandle,
|
||||
NodeTargetHandle,
|
||||
@@ -321,6 +322,11 @@ const BaseNode: FC<BaseNodeProps> = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{data.type === BlockEnum.Tool && (
|
||||
<div className='px-3 pb-2'>
|
||||
<CopyID content={data.provider_id || ''} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
Reference in New Issue
Block a user