diff --git a/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/component.tsx b/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/component.tsx index c872d59f20..79c9757cf7 100644 --- a/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/component.tsx +++ b/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/component.tsx @@ -210,7 +210,6 @@ const ToolBlockComponent = ({ return resultMetadata?.tools?.[configId] }, [activeTabId, configId, fileMetadata, isUsingExternalMetadata, metadata]) const isToolMissing = !currentProvider || !currentTool - const missingFieldCount = toolConfigFromMetadata?.configuration?.fields?.length ?? 0 const isInteractive = editor.isEditable() @@ -339,6 +338,12 @@ const ToolBlockComponent = ({ }, [isSettingOpen, portalContainer, ref, useModalValue]) const displayLabel = label || toolMeta?.label || tool + const missingDisplayLabel = useMemo(() => { + if (!isToolMissing) + return displayLabel + const firstSegment = displayLabel.split('/').map(item => item.trim()).filter(Boolean)[0] + return firstSegment || displayLabel + }, [displayLabel, isToolMissing]) const resolvedIcon = (() => { const fromNode = theme === Theme.dark ? iconDark : icon if (fromNode) @@ -623,13 +628,10 @@ const ToolBlockComponent = ({ > {renderIcon()} - {displayLabel} + {isToolMissing ? missingDisplayLabel : displayLabel} {isToolMissing && ( <> - - {missingFieldCount} - diff --git a/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/tool-group-block-component.tsx b/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/tool-group-block-component.tsx index 9a0ee06cdd..4cb3095773 100644 --- a/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/tool-group-block-component.tsx +++ b/web/app/components/workflow/skill/editor/skill-editor/plugins/tool-block/tool-group-block-component.tsx @@ -4,15 +4,17 @@ import type { ToolParameter } from '@/app/components/tools/types' import type { ToolValue } from '@/app/components/workflow/block-selector/types' import type { ToolWithProvider } from '@/app/components/workflow/types' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import Link from 'next/link' import * as React from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' import { useShallow } from 'zustand/react/shallow' import AppIcon from '@/app/components/base/app-icon' import Modal from '@/app/components/base/modal' import { useSelectOrDelete } from '@/app/components/base/prompt-editor/hooks' import Switch from '@/app/components/base/switch' +import Tooltip from '@/app/components/base/tooltip' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import ToolAuthorizationSection from '@/app/components/plugins/plugin-detail-panel/tool-selector/sections/tool-authorization-section' import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance' @@ -195,6 +197,21 @@ const ToolGroupBlockComponent = ({ } }) }, [currentProvider, language, tools]) + const missingToolCount = useMemo(() => { + if (!tools.length) + return 0 + if (!currentProvider) + return tools.length + const availableToolNames = new Set(currentProvider.tools?.map(item => item.name) || []) + return tools.reduce((count, item) => count + (availableToolNames.has(item.tool) ? 0 : 1), 0) + }, [currentProvider, tools]) + const isToolMissing = missingToolCount > 0 + const missingDisplayLabel = useMemo(() => { + if (!isToolMissing) + return providerLabel + const firstSegment = providerLabel.split('/').map(item => item.trim()).filter(Boolean)[0] + return firstSegment || providerLabel + }, [isToolMissing, providerLabel]) const activeToolItem = useMemo(() => { if (!expandedToolId) @@ -530,6 +547,13 @@ const ToolGroupBlockComponent = ({ }, [activeTabId, currentProvider?.type, fileMetadata, isUsingExternalMetadata, metadata, onMetadataChange, storeApi]) const renderIcon = () => { + if (isToolMissing) { + return ( + + + + ) + } if (!resolvedIcon) return null const iconNode = (() => { @@ -563,7 +587,7 @@ const ToolGroupBlockComponent = ({ @@ -588,6 +612,27 @@ const ToolGroupBlockComponent = ({ ) } + const missingTooltipContent = useMemo(() => { + if (!isToolMissing) + return null + return ( +