diff --git a/web/app/components/workflow/block-selector/__tests__/tabs.spec.tsx b/web/app/components/workflow/block-selector/__tests__/tabs.spec.tsx
index 7e73e1debdf..fc3e4017099 100644
--- a/web/app/components/workflow/block-selector/__tests__/tabs.spec.tsx
+++ b/web/app/components/workflow/block-selector/__tests__/tabs.spec.tsx
@@ -19,21 +19,6 @@ const {
},
}))
-vi.mock('@/app/components/base/tooltip', () => ({
- default: ({
- children,
- popupContent,
- }: {
- children: React.ReactNode
- popupContent: React.ReactNode
- }) => (
-
- {popupContent}
- {children}
-
- ),
-}))
-
vi.mock('@/context/global-public-context', () => ({
useGlobalPublicStore: (selector: (state: { systemFeatures: { enable_marketplace: boolean } }) => unknown) => selector({
systemFeatures: { enable_marketplace: true },
@@ -127,7 +112,7 @@ describe('Tabs', () => {
render()
expect(screen.getByText('start-content')).toBeInTheDocument()
- expect(screen.getByText('workflow.tabs.startDisabledTip')).toBeInTheDocument()
+ expect(screen.getByText('Blocks')).toBeInTheDocument()
})
it('should switch tabs through click handlers and render tools content with normalized icons', () => {
diff --git a/web/app/components/workflow/block-selector/blocks.tsx b/web/app/components/workflow/block-selector/blocks.tsx
index 1d036178522..2d6d219f55a 100644
--- a/web/app/components/workflow/block-selector/blocks.tsx
+++ b/web/app/components/workflow/block-selector/blocks.tsx
@@ -9,7 +9,7 @@ import {
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
import Badge from '@/app/components/base/badge'
-import Tooltip from '@/app/components/base/tooltip'
+import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import BlockIcon from '../block-icon'
import { BlockEnum } from '../types'
import { BLOCK_CLASSIFICATIONS } from './constants'
@@ -93,12 +93,33 @@ const Blocks = ({
}
{
filteredList.map(block => (
-
+ onSelect(block.metaData.type)}
+ >
+
+ {block.metaData.title}
+ {
+ block.metaData.type === BlockEnum.LoopEnd && (
+
+ )
+ }
+
+ )}
+ />
+
{block.metaData.title}
{block.metaData.description}
- )}
- >
- onSelect(block.metaData.type)}
- >
-
-
{block.metaData.title}
- {
- block.metaData.type === BlockEnum.LoopEnd && (
-
- )
- }
-
-
+
+
))
}
diff --git a/web/app/components/workflow/block-selector/featured-tools.tsx b/web/app/components/workflow/block-selector/featured-tools.tsx
index 2491ceca9cc..917f71f95c9 100644
--- a/web/app/components/workflow/block-selector/featured-tools.tsx
+++ b/web/app/components/workflow/block-selector/featured-tools.tsx
@@ -8,7 +8,7 @@ import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows'
import Loading from '@/app/components/base/loading'
-import Tooltip from '@/app/components/base/tooltip'
+import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
import Action from '@/app/components/workflow/block-selector/market-place-plugin/action'
import { useGetLanguage } from '@/context/i18n'
@@ -256,63 +256,65 @@ function FeaturedToolUninstalledItem({
return (
<>
-
+
+
+
+
{installCountLabel}
+
setIsActionHovered(true)}
+ onMouseLeave={() => {
+ if (!actionOpen)
+ setIsActionHovered(false)
+ }}
+ >
+
+
{
+ setActionOpen(value)
+ setIsActionHovered(value)
+ }}
+ author={plugin.org}
+ name={plugin.name}
+ version={plugin.latest_version}
+ />
+
+
+
+ )}
+ />
+
- )}
- disabled={!description || isActionHovered || actionOpen || isInstallModalOpen}
- >
-
-
-
-
{installCountLabel}
-
setIsActionHovered(true)}
- onMouseLeave={() => {
- if (!actionOpen)
- setIsActionHovered(false)
- }}
- >
-
-
{
- setActionOpen(value)
- setIsActionHovered(value)
- }}
- author={plugin.org}
- name={plugin.name}
- version={plugin.latest_version}
- />
-
-
-
-
+
+
{isInstallModalOpen && (
-
+
+
+
+
{installCountLabel}
+
setIsActionHovered(true)}
+ onMouseLeave={() => {
+ if (!actionOpen)
+ setIsActionHovered(false)
+ }}
+ >
+
+
{
+ setActionOpen(value)
+ setIsActionHovered(value)
+ }}
+ author={plugin.org}
+ name={plugin.name}
+ version={plugin.latest_version}
+ />
+
+
+
+ )}
+ />
+
- )}
- disabled={!description || isActionHovered || actionOpen || isInstallModalOpen}
- >
-
-
-
-
{installCountLabel}
-
setIsActionHovered(true)}
- onMouseLeave={() => {
- if (!actionOpen)
- setIsActionHovered(false)
- }}
- >
-
-
{
- setActionOpen(value)
- setIsActionHovered(value)
- }}
- author={plugin.org}
- name={plugin.name}
- version={plugin.latest_version}
- />
-
-
-
-
+
+
{isInstallModalOpen && (
(
-
+ onSelect(block.type)}
+ >
+
+
+ {t(`blocks.${block.type}`, { ns: 'workflow' })}
+ {block.type === BlockEnumValues.Start && (
+ {t('blocks.originalStartNode', { ns: 'workflow' })}
+ )}
+
+
+ )}
+ />
+
)}
- )}
- >
- onSelect(block.type)}
- >
-
-
- {t(`blocks.${block.type}`, { ns: 'workflow' })}
- {block.type === BlockEnumValues.Start && (
- {t('blocks.originalStartNode', { ns: 'workflow' })}
- )}
-
-
-
+
+
), [availableNodesMetaData, onSelect, t])
if (isEmpty)
diff --git a/web/app/components/workflow/block-selector/tabs.tsx b/web/app/components/workflow/block-selector/tabs.tsx
index 50d9f4970c3..521323878c1 100644
--- a/web/app/components/workflow/block-selector/tabs.tsx
+++ b/web/app/components/workflow/block-selector/tabs.tsx
@@ -7,7 +7,7 @@ import type {
} from '../types'
import { memo, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import Tooltip from '@/app/components/base/tooltip'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { useFeaturedToolsRecommendations } from '@/service/use-plugins'
import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools } from '@/service/use-tools'
@@ -128,19 +128,21 @@ const TabHeaderItem = ({
if (tab.disabled) {
return (
-
-
- {tab.name}
-
+
+
+ {tab.name}
+
+ )}
+ />
+
+ {disabledTip}
+
)
}
diff --git a/web/app/components/workflow/block-selector/tool/action-item.tsx b/web/app/components/workflow/block-selector/tool/action-item.tsx
index e95d02e8d78..5ad47eb285f 100644
--- a/web/app/components/workflow/block-selector/tool/action-item.tsx
+++ b/web/app/components/workflow/block-selector/tool/action-item.tsx
@@ -7,7 +7,7 @@ import * as React from 'react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
-import Tooltip from '@/app/components/base/tooltip'
+import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import { useGetLanguage } from '@/context/i18n'
import useTheme from '@/hooks/use-theme'
import { Theme } from '@/types/app'
@@ -58,12 +58,57 @@ const ToolItem: FC = ({
}, [theme, normalizedIcon, normalizedIconDark])
return (
-
+ {
+ if (disabled)
+ return
+ const params: Record = {}
+ if (payload.parameters) {
+ payload.parameters.forEach((item) => {
+ params[item.name] = ''
+ })
+ }
+ onSelect(BlockEnum.Tool, {
+ provider_id: provider.id,
+ provider_type: provider.type,
+ provider_name: provider.name,
+ plugin_id: provider.plugin_id,
+ plugin_unique_identifier: provider.plugin_unique_identifier,
+ provider_icon: normalizedIcon,
+ provider_icon_dark: normalizedIconDark,
+ tool_name: payload.name,
+ tool_label: payload.label[language],
+ tool_description: payload.description[language],
+ title: payload.label[language],
+ is_team_authorization: provider.is_team_authorization,
+ paramSchemas: payload.parameters,
+ params,
+ meta: provider.meta,
+ })
+ trackEvent('tool_selected', {
+ tool_name: payload.name,
+ plugin_id: provider.plugin_id,
+ })
+ }}
+ >
+
+ {payload.label[language]}
+
+ {isAdded && (
+ {t('addToolModal.added', { ns: 'tools' })}
+ )}
+
+ )}
+ />
+
= ({
{payload.label[language]}
{payload.description[language]}
- )}
- >
- {
- if (disabled)
- return
- const params: Record
= {}
- if (payload.parameters) {
- payload.parameters.forEach((item) => {
- params[item.name] = ''
- })
- }
- onSelect(BlockEnum.Tool, {
- provider_id: provider.id,
- provider_type: provider.type,
- provider_name: provider.name,
- plugin_id: provider.plugin_id,
- plugin_unique_identifier: provider.plugin_unique_identifier,
- provider_icon: normalizedIcon,
- provider_icon_dark: normalizedIconDark,
- tool_name: payload.name,
- tool_label: payload.label[language],
- tool_description: payload.description[language],
- title: payload.label[language],
- is_team_authorization: provider.is_team_authorization,
- paramSchemas: payload.parameters,
- params,
- meta: provider.meta,
- })
- trackEvent('tool_selected', {
- tool_name: payload.name,
- plugin_id: provider.plugin_id,
- })
- }}
- >
-
- {payload.label[language]}
-
- {isAdded && (
- {t('addToolModal.added', { ns: 'tools' })}
- )}
-
-
+
+
)
}
export default React.memo(ToolItem)
diff --git a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx
index 38ad4951eaf..fb96888d4a9 100644
--- a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx
+++ b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx
@@ -4,7 +4,7 @@ import type { TriggerDefaultValue, TriggerWithProvider } from '../types'
import type { Event } from '@/app/components/tools/types'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import Tooltip from '@/app/components/base/tooltip'
+import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import { useGetLanguage } from '@/context/i18n'
import { cn } from '@/utils/classnames'
import BlockIcon from '../../block-icon'
@@ -29,12 +29,51 @@ const TriggerPluginActionItem: FC = ({
const language = useGetLanguage()
return (
-
+ {
+ if (disabled)
+ return
+ const params: Record = {}
+ if (payload.parameters) {
+ payload.parameters.forEach((item: any) => {
+ params[item.name] = ''
+ })
+ }
+ onSelect(BlockEnum.TriggerPlugin, {
+ plugin_id: provider.plugin_id,
+ provider_id: provider.name,
+ provider_type: provider.type as string,
+ provider_name: provider.name,
+ event_name: payload.name,
+ event_label: payload.label[language],
+ event_description: payload.description[language],
+ plugin_unique_identifier: provider.plugin_unique_identifier,
+ title: payload.label[language],
+ is_team_authorization: provider.is_team_authorization,
+ output_schema: payload.output_schema || {},
+ paramSchemas: payload.parameters,
+ params,
+ meta: provider.meta,
+ })
+ }}
+ >
+
+ {payload.label[language]}
+
+ {isAdded && (
+ {t('addToolModal.added', { ns: 'tools' })}
+ )}
+
+ )}
+ />
+
= ({
{payload.label[language]}
{payload.description[language]}
- )}
- >
- {
- if (disabled)
- return
- const params: Record
= {}
- if (payload.parameters) {
- payload.parameters.forEach((item: any) => {
- params[item.name] = ''
- })
- }
- onSelect(BlockEnum.TriggerPlugin, {
- plugin_id: provider.plugin_id,
- provider_id: provider.name,
- provider_type: provider.type as string,
- provider_name: provider.name,
- event_name: payload.name,
- event_label: payload.label[language],
- event_description: payload.description[language],
- plugin_unique_identifier: provider.plugin_unique_identifier,
- title: payload.label[language],
- is_team_authorization: provider.is_team_authorization,
- output_schema: payload.output_schema || {},
- paramSchemas: payload.parameters,
- params,
- meta: provider.meta,
- })
- }}
- >
-
- {payload.label[language]}
-
- {isAdded && (
- {t('addToolModal.added', { ns: 'tools' })}
- )}
-
-
+
+
)
}
export default React.memo(TriggerPluginActionItem)
diff --git a/web/app/components/workflow/nodes/question-classifier/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/question-classifier/__tests__/integration.spec.tsx
index c11f78bc087..a151a06d1bf 100644
--- a/web/app/components/workflow/nodes/question-classifier/__tests__/integration.spec.tsx
+++ b/web/app/components/workflow/nodes/question-classifier/__tests__/integration.spec.tsx
@@ -345,7 +345,7 @@ describe('question-classifier path', () => {
expect(screen.getByText(`${longName.slice(0, 50)}...`)).toBeInTheDocument()
await user.hover(screen.getByText(`${longName.slice(0, 50)}...`))
- expect(screen.getByText(longName)).toBeInTheDocument()
+ expect(await screen.findByText(longName)).toBeInTheDocument()
rerender(
= ({
title={(
{t(`${i18nPrefix}.instruction`, { ns: 'workflow' })}
-
+
+
+
+ )}
+ />
+
{t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
- )}
- triggerClassName="w-3.5 h-3.5 ml-0.5"
- />
+
+
)}
value={instruction}
diff --git a/web/app/components/workflow/nodes/question-classifier/node.tsx b/web/app/components/workflow/nodes/question-classifier/node.tsx
index 38ad4e75c69..37dfa697cc6 100644
--- a/web/app/components/workflow/nodes/question-classifier/node.tsx
+++ b/web/app/components/workflow/nodes/question-classifier/node.tsx
@@ -4,7 +4,7 @@ import type { NodeProps } from 'reactflow'
import type { QuestionClassifierNodeType } from './types'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import Tooltip from '@/app/components/base/tooltip'
+import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import {
useTextGenerationCurrentProviderAndModelAndModelList,
} from '@/app/components/header/account-setting/model-provider-page/hooks'
@@ -47,15 +47,18 @@ const TruncatedClassItem: FC = ({ topic, index, nodeId,
{shouldShowTooltip
? (
-
+
+
- )}
- >
- {content}
-
+
+
)
: content}
diff --git a/web/app/components/workflow/operator/tip-popup.tsx b/web/app/components/workflow/operator/tip-popup.tsx
index 4130db9071b..c0381831b73 100644
--- a/web/app/components/workflow/operator/tip-popup.tsx
+++ b/web/app/components/workflow/operator/tip-popup.tsx
@@ -1,10 +1,11 @@
+import type { ReactElement } from 'react'
import { memo } from 'react'
-import Tooltip from '@/app/components/base/tooltip'
+import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import ShortcutsName from '../shortcuts-name'
type TipPopupProps = {
title: string
- children: React.ReactNode
+ children: ReactElement
shortcuts?: string[]
}
const TipPopup = ({
@@ -13,21 +14,17 @@ const TipPopup = ({
shortcuts,
}: TipPopupProps) => {
return (
-
+
+
{title}
{
shortcuts &&
}
- )}
- >
- {children}
-
+
+
)
}
diff --git a/web/app/components/workflow/run/node.tsx b/web/app/components/workflow/run/node.tsx
index 3075ec6a175..4fac36c0814 100644
--- a/web/app/components/workflow/run/node.tsx
+++ b/web/app/components/workflow/run/node.tsx
@@ -18,7 +18,7 @@ import {
} from '@remixicon/react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import Tooltip from '@/app/components/base/tooltip'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
@@ -142,18 +142,21 @@ const NodePanel: FC = ({
type={nodeInfo.node_type}
toolIcon={((nodeInfo.extras as { icon?: string } | undefined)?.icon || nodeInfo.extras) as string | { content: string, background: string } | undefined}
/>
-
+
+ {nodeInfo.title}
+
+ )}
+ />
+
{nodeInfo.title}
- }
- >
-
- {nodeInfo.title}
-
+
{!['running', 'paused'].includes(nodeInfo.status) && !hideInfo && (