mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 16:17:25 +08:00
fix(web): migrate workflow overlay previews
This commit is contained in:
@@ -19,21 +19,6 @@ const {
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/tooltip', () => ({
|
||||
default: ({
|
||||
children,
|
||||
popupContent,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
popupContent: React.ReactNode
|
||||
}) => (
|
||||
<div>
|
||||
<span>{popupContent}</span>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
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(<Tabs {...baseProps} />)
|
||||
|
||||
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', () => {
|
||||
|
||||
@@ -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 => (
|
||||
<Tooltip
|
||||
key={block.metaData.type}
|
||||
position="right"
|
||||
popupClassName="w-[200px] rounded-xl"
|
||||
needsDelay={false}
|
||||
popupContent={(
|
||||
<Popover key={block.metaData.type}>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
render={(
|
||||
<div
|
||||
key={block.metaData.type}
|
||||
className="flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover"
|
||||
onClick={() => onSelect(block.metaData.type)}
|
||||
>
|
||||
<BlockIcon
|
||||
className="mr-2 shrink-0"
|
||||
type={block.metaData.iconType || block.metaData.type}
|
||||
/>
|
||||
<div className="grow text-sm text-text-secondary">{block.metaData.title}</div>
|
||||
{
|
||||
block.metaData.type === BlockEnum.LoopEnd && (
|
||||
<Badge
|
||||
text={t('nodes.loop.loopNode', { ns: 'workflow' })}
|
||||
className="ml-2 shrink-0"
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent placement="right" popupClassName="w-[200px] rounded-xl px-3 py-2 text-left">
|
||||
<div>
|
||||
<BlockIcon
|
||||
size="md"
|
||||
@@ -108,28 +129,8 @@ const Blocks = ({
|
||||
<div className="mb-1 text-text-primary system-md-medium">{block.metaData.title}</div>
|
||||
<div className="text-text-tertiary system-xs-regular">{block.metaData.description}</div>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
key={block.metaData.type}
|
||||
className="flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover"
|
||||
onClick={() => onSelect(block.metaData.type)}
|
||||
>
|
||||
<BlockIcon
|
||||
className="mr-2 shrink-0"
|
||||
type={block.metaData.iconType || block.metaData.type}
|
||||
/>
|
||||
<div className="grow text-sm text-text-secondary">{block.metaData.title}</div>
|
||||
{
|
||||
block.metaData.type === BlockEnum.LoopEnd && (
|
||||
<Badge
|
||||
text={t('nodes.loop.loopNode', { ns: 'workflow' })}
|
||||
className="ml-2 shrink-0"
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<Tooltip
|
||||
position="right"
|
||||
needsDelay={false}
|
||||
popupClassName="!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg"
|
||||
popupContent={(
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
disabled={!description || isActionHovered || actionOpen || isInstallModalOpen}
|
||||
render={(
|
||||
<div
|
||||
className="group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover"
|
||||
>
|
||||
<div className="flex h-full min-w-0 items-center">
|
||||
<BlockIcon type={BlockEnum.Tool} toolIcon={plugin.icon} />
|
||||
<div className="ml-2 min-w-0">
|
||||
<div className="truncate text-text-secondary system-sm-medium">{label}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-auto flex h-full items-center gap-1 pl-1">
|
||||
<span className={`text-text-tertiary system-xs-regular ${actionOpen ? 'hidden' : 'group-hover:hidden'}`}>{installCountLabel}</span>
|
||||
<div
|
||||
className={`flex h-full items-center gap-1 text-components-button-secondary-accent-text system-xs-medium [&_.action-btn]:h-6 [&_.action-btn]:min-h-0 [&_.action-btn]:w-6 [&_.action-btn]:rounded-lg [&_.action-btn]:p-0 ${actionOpen ? '' : 'hidden group-hover:flex'}`}
|
||||
onMouseEnter={() => setIsActionHovered(true)}
|
||||
onMouseLeave={() => {
|
||||
if (!actionOpen)
|
||||
setIsActionHovered(false)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
setActionOpen(false)
|
||||
setIsInstallModalOpen(true)
|
||||
setIsActionHovered(true)
|
||||
}}
|
||||
>
|
||||
{t('installAction', { ns: 'plugin' })}
|
||||
</button>
|
||||
<Action
|
||||
open={actionOpen}
|
||||
onOpenChange={(value) => {
|
||||
setActionOpen(value)
|
||||
setIsActionHovered(value)
|
||||
}}
|
||||
author={plugin.org}
|
||||
name={plugin.name}
|
||||
version={plugin.latest_version}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent placement="right" popupClassName="!w-[224px] !rounded-xl !border-[0.5px] !border-black/5 !p-0 !px-3 !py-2.5 !text-xs !leading-[18px] !text-gray-700 !shadow-lg">
|
||||
<div>
|
||||
<BlockIcon size="md" className="mb-2" type={BlockEnum.Tool} toolIcon={plugin.icon} />
|
||||
<div className="mb-1 text-sm leading-5 text-text-primary">{label}</div>
|
||||
<div className="text-xs leading-[18px] text-text-secondary">{description}</div>
|
||||
</div>
|
||||
)}
|
||||
disabled={!description || isActionHovered || actionOpen || isInstallModalOpen}
|
||||
>
|
||||
<div
|
||||
className="group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover"
|
||||
>
|
||||
<div className="flex h-full min-w-0 items-center">
|
||||
<BlockIcon type={BlockEnum.Tool} toolIcon={plugin.icon} />
|
||||
<div className="ml-2 min-w-0">
|
||||
<div className="truncate text-text-secondary system-sm-medium">{label}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-auto flex h-full items-center gap-1 pl-1">
|
||||
<span className={`text-text-tertiary system-xs-regular ${actionOpen ? 'hidden' : 'group-hover:hidden'}`}>{installCountLabel}</span>
|
||||
<div
|
||||
className={`flex h-full items-center gap-1 text-components-button-secondary-accent-text system-xs-medium [&_.action-btn]:h-6 [&_.action-btn]:min-h-0 [&_.action-btn]:w-6 [&_.action-btn]:rounded-lg [&_.action-btn]:p-0 ${actionOpen ? '' : 'hidden group-hover:flex'}`}
|
||||
onMouseEnter={() => setIsActionHovered(true)}
|
||||
onMouseLeave={() => {
|
||||
if (!actionOpen)
|
||||
setIsActionHovered(false)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
setActionOpen(false)
|
||||
setIsInstallModalOpen(true)
|
||||
setIsActionHovered(true)
|
||||
}}
|
||||
>
|
||||
{t('installAction', { ns: 'plugin' })}
|
||||
</button>
|
||||
<Action
|
||||
open={actionOpen}
|
||||
onOpenChange={(value) => {
|
||||
setActionOpen(value)
|
||||
setIsActionHovered(value)
|
||||
}}
|
||||
author={plugin.org}
|
||||
name={plugin.name}
|
||||
version={plugin.latest_version}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{isInstallModalOpen && (
|
||||
<InstallFromMarketplace
|
||||
uniqueIdentifier={plugin.latest_package_identifier}
|
||||
|
||||
@@ -7,7 +7,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'
|
||||
@@ -251,63 +251,65 @@ function FeaturedTriggerUninstalledItem({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
position="right"
|
||||
needsDelay={false}
|
||||
popupClassName="!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg"
|
||||
popupContent={(
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
disabled={!description || isActionHovered || actionOpen || isInstallModalOpen}
|
||||
render={(
|
||||
<div
|
||||
className="group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover"
|
||||
>
|
||||
<div className="flex h-full min-w-0 items-center">
|
||||
<BlockIcon type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} />
|
||||
<div className="ml-2 min-w-0">
|
||||
<div className="truncate text-text-secondary system-sm-medium">{label}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-auto flex h-full items-center gap-1 pl-1">
|
||||
<span className={`text-text-tertiary system-xs-regular ${actionOpen ? 'hidden' : 'group-hover:hidden'}`}>{installCountLabel}</span>
|
||||
<div
|
||||
className={`flex h-full items-center gap-1 text-components-button-secondary-accent-text system-xs-medium [&_.action-btn]:h-6 [&_.action-btn]:min-h-0 [&_.action-btn]:w-6 [&_.action-btn]:rounded-lg [&_.action-btn]:p-0 ${actionOpen ? '' : 'hidden group-hover:flex'}`}
|
||||
onMouseEnter={() => setIsActionHovered(true)}
|
||||
onMouseLeave={() => {
|
||||
if (!actionOpen)
|
||||
setIsActionHovered(false)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
setActionOpen(false)
|
||||
setIsInstallModalOpen(true)
|
||||
setIsActionHovered(true)
|
||||
}}
|
||||
>
|
||||
{t('installAction', { ns: 'plugin' })}
|
||||
</button>
|
||||
<Action
|
||||
open={actionOpen}
|
||||
onOpenChange={(value) => {
|
||||
setActionOpen(value)
|
||||
setIsActionHovered(value)
|
||||
}}
|
||||
author={plugin.org}
|
||||
name={plugin.name}
|
||||
version={plugin.latest_version}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent placement="right" popupClassName="!w-[224px] !rounded-xl !border-[0.5px] !border-black/5 !p-0 !px-3 !py-2.5 !text-xs !leading-[18px] !text-gray-700 !shadow-lg">
|
||||
<div>
|
||||
<BlockIcon size="md" className="mb-2" type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} />
|
||||
<div className="mb-1 text-sm leading-5 text-text-primary">{label}</div>
|
||||
<div className="text-xs leading-[18px] text-text-secondary">{description}</div>
|
||||
</div>
|
||||
)}
|
||||
disabled={!description || isActionHovered || actionOpen || isInstallModalOpen}
|
||||
>
|
||||
<div
|
||||
className="group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover"
|
||||
>
|
||||
<div className="flex h-full min-w-0 items-center">
|
||||
<BlockIcon type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} />
|
||||
<div className="ml-2 min-w-0">
|
||||
<div className="truncate text-text-secondary system-sm-medium">{label}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-auto flex h-full items-center gap-1 pl-1">
|
||||
<span className={`text-text-tertiary system-xs-regular ${actionOpen ? 'hidden' : 'group-hover:hidden'}`}>{installCountLabel}</span>
|
||||
<div
|
||||
className={`flex h-full items-center gap-1 text-components-button-secondary-accent-text system-xs-medium [&_.action-btn]:h-6 [&_.action-btn]:min-h-0 [&_.action-btn]:w-6 [&_.action-btn]:rounded-lg [&_.action-btn]:p-0 ${actionOpen ? '' : 'hidden group-hover:flex'}`}
|
||||
onMouseEnter={() => setIsActionHovered(true)}
|
||||
onMouseLeave={() => {
|
||||
if (!actionOpen)
|
||||
setIsActionHovered(false)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
setActionOpen(false)
|
||||
setIsInstallModalOpen(true)
|
||||
setIsActionHovered(true)
|
||||
}}
|
||||
>
|
||||
{t('installAction', { ns: 'plugin' })}
|
||||
</button>
|
||||
<Action
|
||||
open={actionOpen}
|
||||
onOpenChange={(value) => {
|
||||
setActionOpen(value)
|
||||
setIsActionHovered(value)
|
||||
}}
|
||||
author={plugin.org}
|
||||
name={plugin.name}
|
||||
version={plugin.latest_version}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{isInstallModalOpen && (
|
||||
<InstallFromMarketplace
|
||||
uniqueIdentifier={plugin.latest_package_identifier}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useMemo,
|
||||
} 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 useNodes from '@/app/components/workflow/store/workflow/use-nodes'
|
||||
import { useAvailableNodesMetaData } from '../../workflow-app/hooks'
|
||||
import BlockIcon from '../block-icon'
|
||||
@@ -68,12 +68,29 @@ const StartBlocks = ({
|
||||
}, [isEmpty, onContentStateChange])
|
||||
|
||||
const renderBlock = useCallback((block: typeof START_BLOCKS[number]) => (
|
||||
<Tooltip
|
||||
key={block.type}
|
||||
position="right"
|
||||
popupClassName="w-[224px] rounded-xl"
|
||||
needsDelay={false}
|
||||
popupContent={(
|
||||
<Popover key={block.type}>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
render={(
|
||||
<div
|
||||
className="flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover"
|
||||
onClick={() => onSelect(block.type)}
|
||||
>
|
||||
<BlockIcon
|
||||
className="mr-2 shrink-0"
|
||||
type={block.type}
|
||||
/>
|
||||
<div className="flex w-0 grow items-center justify-between text-sm text-text-secondary">
|
||||
<span className="truncate">{t(`blocks.${block.type}`, { ns: 'workflow' })}</span>
|
||||
{block.type === BlockEnumValues.Start && (
|
||||
<span className="ml-2 shrink-0 text-text-quaternary system-xs-regular">{t('blocks.originalStartNode', { ns: 'workflow' })}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent placement="right" popupClassName="w-[224px] rounded-xl px-3 py-2 text-left">
|
||||
<div>
|
||||
<BlockIcon
|
||||
size="md"
|
||||
@@ -96,24 +113,8 @@ const StartBlocks = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover"
|
||||
onClick={() => onSelect(block.type)}
|
||||
>
|
||||
<BlockIcon
|
||||
className="mr-2 shrink-0"
|
||||
type={block.type}
|
||||
/>
|
||||
<div className="flex w-0 grow items-center justify-between text-sm text-text-secondary">
|
||||
<span className="truncate">{t(`blocks.${block.type}`, { ns: 'workflow' })}</span>
|
||||
{block.type === BlockEnumValues.Start && (
|
||||
<span className="ml-2 shrink-0 text-text-quaternary system-xs-regular">{t('blocks.originalStartNode', { ns: 'workflow' })}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
), [availableNodesMetaData, onSelect, t])
|
||||
|
||||
if (isEmpty)
|
||||
|
||||
@@ -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 (
|
||||
<Tooltip
|
||||
key={tab.key}
|
||||
position="top"
|
||||
popupClassName="max-w-[200px]"
|
||||
popupContent={disabledTip}
|
||||
>
|
||||
<div
|
||||
className={className}
|
||||
aria-disabled={tab.disabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{tab.name}
|
||||
</div>
|
||||
<Tooltip key={tab.key}>
|
||||
<TooltipTrigger
|
||||
render={(
|
||||
<div
|
||||
className={className}
|
||||
aria-disabled={tab.disabled}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{tab.name}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<TooltipContent placement="top" popupClassName="max-w-[200px]">
|
||||
{disabledTip}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<Props> = ({
|
||||
}, [theme, normalizedIcon, normalizedIconDark])
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={payload.name}
|
||||
position="right"
|
||||
needsDelay={false}
|
||||
popupClassName="!p-0 !px-3 !py-2.5 !w-[200px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg"
|
||||
popupContent={(
|
||||
<Popover key={payload.name}>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
render={(
|
||||
<div
|
||||
key={payload.name}
|
||||
data-tool-picker-item="true"
|
||||
className="flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
if (disabled)
|
||||
return
|
||||
const params: Record<string, string> = {}
|
||||
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,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className={cn('truncate border-l-2 border-divider-subtle py-2 pl-4 text-text-secondary system-sm-medium')}>
|
||||
<span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span>
|
||||
</div>
|
||||
{isAdded && (
|
||||
<div className="mr-4 text-text-tertiary system-xs-regular">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent placement="right" popupClassName="!w-[200px] !rounded-xl !border-[0.5px] !border-black/5 !p-0 !px-3 !py-2.5 !text-xs !leading-[18px] !text-gray-700 !shadow-lg">
|
||||
<div>
|
||||
<BlockIcon
|
||||
size="md"
|
||||
@@ -74,52 +119,8 @@ const ToolItem: FC<Props> = ({
|
||||
<div className="mb-1 text-sm leading-5 text-text-primary">{payload.label[language]}</div>
|
||||
<div className="text-xs leading-[18px] text-text-secondary">{payload.description[language]}</div>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
key={payload.name}
|
||||
data-tool-picker-item="true"
|
||||
className="flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
if (disabled)
|
||||
return
|
||||
const params: Record<string, string> = {}
|
||||
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,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className={cn('truncate border-l-2 border-divider-subtle py-2 pl-4 text-text-secondary system-sm-medium')}>
|
||||
<span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span>
|
||||
</div>
|
||||
{isAdded && (
|
||||
<div className="mr-4 text-text-tertiary system-xs-regular">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
export default React.memo(ToolItem)
|
||||
|
||||
@@ -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<Props> = ({
|
||||
const language = useGetLanguage()
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={payload.name}
|
||||
position="right"
|
||||
needsDelay={false}
|
||||
popupClassName="!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg"
|
||||
popupContent={(
|
||||
<Popover key={payload.name}>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
render={(
|
||||
<div
|
||||
key={payload.name}
|
||||
className="flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
if (disabled)
|
||||
return
|
||||
const params: Record<string, string> = {}
|
||||
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,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className={cn('truncate border-l-2 border-divider-subtle py-2 pl-4 text-text-secondary system-sm-medium')}>
|
||||
<span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span>
|
||||
</div>
|
||||
{isAdded && (
|
||||
<div className="mr-4 text-text-tertiary system-xs-regular">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent placement="right" popupClassName="!w-[224px] !rounded-xl !border-[0.5px] !border-black/5 !p-0 !px-3 !py-2.5 !text-xs !leading-[18px] !text-gray-700 !shadow-lg">
|
||||
<div>
|
||||
<BlockIcon
|
||||
size="md"
|
||||
@@ -45,46 +84,8 @@ const TriggerPluginActionItem: FC<Props> = ({
|
||||
<div className="mb-1 text-sm leading-5 text-text-primary">{payload.label[language]}</div>
|
||||
<div className="text-xs leading-[18px] text-text-secondary">{payload.description[language]}</div>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
key={payload.name}
|
||||
className="flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
if (disabled)
|
||||
return
|
||||
const params: Record<string, string> = {}
|
||||
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,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className={cn('truncate border-l-2 border-divider-subtle py-2 pl-4 text-text-secondary system-sm-medium')}>
|
||||
<span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span>
|
||||
</div>
|
||||
{isAdded && (
|
||||
<div className="mr-4 text-text-tertiary system-xs-regular">{t('addToolModal.added', { ns: 'tools' })}</div>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
export default React.memo(TriggerPluginActionItem)
|
||||
|
||||
@@ -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(
|
||||
<Node
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { FC } from 'react'
|
||||
import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/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 Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import MemoryConfig from '../../_base/components/memory-config'
|
||||
|
||||
@@ -48,14 +48,22 @@ const AdvancedSetting: FC<Props> = ({
|
||||
title={(
|
||||
<div className="flex items-center space-x-1">
|
||||
<span className="uppercase">{t(`${i18nPrefix}.instruction`, { ns: 'workflow' })}</span>
|
||||
<Tooltip
|
||||
popupContent={(
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
render={(
|
||||
<span className="ml-0.5 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary hover:text-text-tertiary" />
|
||||
</span>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent placement="top" popupClassName="p-2">
|
||||
<div className="w-[120px]">
|
||||
{t(`${i18nPrefix}.instructionTip`, { ns: 'workflow' })}
|
||||
</div>
|
||||
)}
|
||||
triggerClassName="w-3.5 h-3.5 ml-0.5"
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
)}
|
||||
value={instruction}
|
||||
|
||||
@@ -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<TruncatedClassItemProps> = ({ topic, index, nodeId,
|
||||
</div>
|
||||
{shouldShowTooltip
|
||||
? (
|
||||
<Tooltip
|
||||
popupContent={(
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
nativeButton={false}
|
||||
render={content}
|
||||
/>
|
||||
<PopoverContent placement="top" popupClassName="p-2">
|
||||
<div className="max-w-[300px] break-words">
|
||||
<ReadonlyInputWithSelectVar value={topic.name} nodeId={nodeId} />
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
: content}
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
<Tooltip
|
||||
needsDelay={false}
|
||||
offset={4}
|
||||
popupClassName="p-0 bg-transparent"
|
||||
popupContent={(
|
||||
<Popover>
|
||||
<PopoverTrigger openOnHover nativeButton={false} render={children} />
|
||||
<PopoverContent placement="top" sideOffset={4} popupClassName="border-none bg-transparent p-0 shadow-none">
|
||||
<div className="flex items-center gap-1 rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg p-1.5 shadow-lg backdrop-blur-[5px]">
|
||||
<span className="text-text-secondary system-xs-medium">{title}</span>
|
||||
{
|
||||
shortcuts && <ShortcutsName keys={shortcuts} />
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Tooltip>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Props> = ({
|
||||
type={nodeInfo.node_type}
|
||||
toolIcon={((nodeInfo.extras as { icon?: string } | undefined)?.icon || nodeInfo.extras) as string | { content: string, background: string } | undefined}
|
||||
/>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={(
|
||||
<div className={cn(
|
||||
'grow truncate text-text-secondary system-xs-semibold-uppercase',
|
||||
hideInfo && '!text-xs',
|
||||
)}
|
||||
>
|
||||
{nodeInfo.title}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<TooltipContent>
|
||||
<div className="max-w-xs">{nodeInfo.title}</div>
|
||||
}
|
||||
>
|
||||
<div className={cn(
|
||||
'grow truncate text-text-secondary system-xs-semibold-uppercase',
|
||||
hideInfo && '!text-xs',
|
||||
)}
|
||||
>
|
||||
{nodeInfo.title}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
{!['running', 'paused'].includes(nodeInfo.status) && !hideInfo && (
|
||||
<div className="shrink-0 text-text-tertiary system-xs-regular">
|
||||
|
||||
Reference in New Issue
Block a user