mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 12:37:20 +08:00
fix: improve test run menu and checklist ui (#25300)
This commit is contained in:
parent
a91e59d544
commit
58cbd337b5
@ -119,8 +119,8 @@ const WorkflowChecklist = ({
|
|||||||
<span className='grow truncate'>
|
<span className='grow truncate'>
|
||||||
{node.title}
|
{node.title}
|
||||||
</span>
|
</span>
|
||||||
<div className='flex h-4 w-[50px] items-center justify-center gap-1 opacity-0 transition-opacity duration-200 group-hover:opacity-100'>
|
<div className='flex h-4 w-[60px] shrink-0 items-center justify-center gap-1 opacity-0 transition-opacity duration-200 group-hover:opacity-100'>
|
||||||
<span className='text-xs font-medium leading-4 text-primary-600'>
|
<span className='whitespace-nowrap text-xs font-medium leading-4 text-primary-600'>
|
||||||
{t('workflow.panel.goTo')}
|
{t('workflow.panel.goTo')}
|
||||||
</span>
|
</span>
|
||||||
<IconR className='h-3.5 w-3.5 text-primary-600' />
|
<IconR className='h-3.5 w-3.5 text-primary-600' />
|
||||||
|
|||||||
@ -16,8 +16,8 @@ import {
|
|||||||
import { WorkflowRunningStatus } from '../types'
|
import { WorkflowRunningStatus } from '../types'
|
||||||
import ViewHistory from './view-history'
|
import ViewHistory from './view-history'
|
||||||
import Checklist from './checklist'
|
import Checklist from './checklist'
|
||||||
import TestRunDropdown, { type TestRunDropdownRef } from './test-run-dropdown'
|
import TestRunMenu, { type TestRunMenuRef } from './test-run-menu'
|
||||||
import type { TriggerOption } from './test-run-dropdown'
|
import type { TriggerOption } from './test-run-menu'
|
||||||
import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options'
|
import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import {
|
import {
|
||||||
@ -36,12 +36,12 @@ const RunMode = memo(() => {
|
|||||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||||
const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running
|
const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running
|
||||||
const dynamicOptions = useDynamicTestRunOptions()
|
const dynamicOptions = useDynamicTestRunOptions()
|
||||||
const testRunDropdownRef = useRef<TestRunDropdownRef>(null)
|
const testRunMenuRef = useRef<TestRunMenuRef>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// @ts-expect-error - Dynamic property for backward compatibility with keyboard shortcuts
|
// @ts-expect-error - Dynamic property for backward compatibility with keyboard shortcuts
|
||||||
window._toggleTestRunDropdown = () => {
|
window._toggleTestRunDropdown = () => {
|
||||||
testRunDropdownRef.current?.toggle()
|
testRunMenuRef.current?.toggle()
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
// @ts-expect-error - Dynamic property cleanup
|
// @ts-expect-error - Dynamic property cleanup
|
||||||
@ -89,8 +89,8 @@ const RunMode = memo(() => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<TestRunDropdown
|
<TestRunMenu
|
||||||
ref={testRunDropdownRef}
|
ref={testRunMenuRef}
|
||||||
options={dynamicOptions}
|
options={dynamicOptions}
|
||||||
onSelect={handleTriggerSelect}
|
onSelect={handleTriggerSelect}
|
||||||
>
|
>
|
||||||
@ -105,7 +105,7 @@ const RunMode = memo(() => {
|
|||||||
{t('workflow.common.run')}
|
{t('workflow.common.run')}
|
||||||
<ShortcutsName keys={['alt', 'r']} className="ml-1" textColor="secondary" />
|
<ShortcutsName keys={['alt', 'r']} className="ml-1" textColor="secondary" />
|
||||||
</div>
|
</div>
|
||||||
</TestRunDropdown>
|
</TestRunMenu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|||||||
@ -22,17 +22,17 @@ export type TestRunOptions = {
|
|||||||
runAll?: TriggerOption
|
runAll?: TriggerOption
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestRunDropdownProps = {
|
type TestRunMenuProps = {
|
||||||
options: TestRunOptions
|
options: TestRunOptions
|
||||||
onSelect: (option: TriggerOption) => void
|
onSelect: (option: TriggerOption) => void
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TestRunDropdownRef = {
|
export type TestRunMenuRef = {
|
||||||
toggle: () => void
|
toggle: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestRunDropdown = forwardRef<TestRunDropdownRef, TestRunDropdownProps>(({
|
const TestRunMenu = forwardRef<TestRunMenuRef, TestRunMenuProps>(({
|
||||||
options,
|
options,
|
||||||
onSelect,
|
onSelect,
|
||||||
children,
|
children,
|
||||||
@ -107,6 +107,6 @@ const TestRunDropdown = forwardRef<TestRunDropdownRef, TestRunDropdownProps>(({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
TestRunDropdown.displayName = 'TestRunDropdown'
|
TestRunMenu.displayName = 'TestRunMenu'
|
||||||
|
|
||||||
export default TestRunDropdown
|
export default TestRunMenu
|
||||||
@ -3,14 +3,12 @@ import { useNodes } from 'reactflow'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { BlockEnum, type CommonNodeType } from '../types'
|
import { BlockEnum, type CommonNodeType } from '../types'
|
||||||
import { getWorkflowEntryNode } from '../utils/workflow-entry'
|
import { getWorkflowEntryNode } from '../utils/workflow-entry'
|
||||||
import type { TestRunOptions, TriggerOption } from '../header/test-run-dropdown'
|
import type { TestRunOptions, TriggerOption } from '../header/test-run-menu'
|
||||||
import Home from '@/app/components/base/icons/src/vender/workflow/Home'
|
import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
|
||||||
import { Schedule, TriggerAll, WebhookLine } from '@/app/components/base/icons/src/vender/workflow'
|
import BlockIcon from '../block-icon'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
import { canFindTool } from '@/utils'
|
import { canFindTool } from '@/utils'
|
||||||
import { CollectionType } from '@/app/components/tools/types'
|
import { useAllTriggerPlugins } from '@/service/use-triggers'
|
||||||
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
|
|
||||||
|
|
||||||
export const useDynamicTestRunOptions = (): TestRunOptions => {
|
export const useDynamicTestRunOptions = (): TestRunOptions => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -19,7 +17,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
|
|||||||
const customTools = useStore(s => s.customTools)
|
const customTools = useStore(s => s.customTools)
|
||||||
const workflowTools = useStore(s => s.workflowTools)
|
const workflowTools = useStore(s => s.workflowTools)
|
||||||
const mcpTools = useStore(s => s.mcpTools)
|
const mcpTools = useStore(s => s.mcpTools)
|
||||||
const { getIconUrl } = useGetIcon()
|
const { data: triggerPlugins } = useAllTriggerPlugins()
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const allTriggers: TriggerOption[] = []
|
const allTriggers: TriggerOption[] = []
|
||||||
@ -36,9 +34,10 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
|
|||||||
type: 'user_input',
|
type: 'user_input',
|
||||||
name: nodeData.title || t('workflow.blocks.start'),
|
name: nodeData.title || t('workflow.blocks.start'),
|
||||||
icon: (
|
icon: (
|
||||||
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-blue-brand-blue-brand-500 text-white shadow-md">
|
<BlockIcon
|
||||||
<Home className="h-3.5 w-3.5" />
|
type={BlockEnum.Start}
|
||||||
</div>
|
size='md'
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@ -50,9 +49,10 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
|
|||||||
type: 'schedule',
|
type: 'schedule',
|
||||||
name: nodeData.title || t('workflow.blocks.trigger-schedule'),
|
name: nodeData.title || t('workflow.blocks.trigger-schedule'),
|
||||||
icon: (
|
icon: (
|
||||||
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-violet-violet-500 text-white shadow-md">
|
<BlockIcon
|
||||||
<Schedule className="h-4.5 w-4.5" />
|
type={BlockEnum.TriggerSchedule}
|
||||||
</div>
|
size='md'
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@ -64,61 +64,30 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
|
|||||||
type: 'webhook',
|
type: 'webhook',
|
||||||
name: nodeData.title || t('workflow.blocks.trigger-webhook'),
|
name: nodeData.title || t('workflow.blocks.trigger-webhook'),
|
||||||
icon: (
|
icon: (
|
||||||
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-blue-blue-500 text-white shadow-md">
|
<BlockIcon
|
||||||
<WebhookLine className="h-4.5 w-4.5" />
|
type={BlockEnum.TriggerWebhook}
|
||||||
</div>
|
size='md'
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
nodeId: node.id,
|
nodeId: node.id,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else if (nodeData.type === BlockEnum.TriggerPlugin) {
|
else if (nodeData.type === BlockEnum.TriggerPlugin) {
|
||||||
let icon
|
|
||||||
let toolIcon: string | any
|
let toolIcon: string | any
|
||||||
|
|
||||||
// 按照 use-workflow-search.tsx 的模式获取工具图标
|
|
||||||
if (nodeData.provider_id) {
|
if (nodeData.provider_id) {
|
||||||
let targetTools = workflowTools
|
const targetTools = triggerPlugins || []
|
||||||
if (nodeData.provider_type === CollectionType.builtIn)
|
|
||||||
targetTools = buildInTools
|
|
||||||
else if (nodeData.provider_type === CollectionType.custom)
|
|
||||||
targetTools = customTools
|
|
||||||
else if (nodeData.provider_type === CollectionType.mcp)
|
|
||||||
targetTools = mcpTools
|
|
||||||
|
|
||||||
toolIcon = targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, nodeData.provider_id!))?.icon
|
toolIcon = targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, nodeData.provider_id!))?.icon
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof toolIcon === 'string') {
|
const icon = (
|
||||||
const iconUrl = toolIcon.startsWith('http') ? toolIcon : getIconUrl(toolIcon)
|
<BlockIcon
|
||||||
icon = (
|
type={BlockEnum.TriggerPlugin}
|
||||||
<div
|
size='md'
|
||||||
className="bg-util-colors-white-white-500 flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 text-white shadow-md"
|
toolIcon={toolIcon}
|
||||||
style={{
|
/>
|
||||||
backgroundImage: `url(${iconUrl})`,
|
)
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundPosition: 'center',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else if (toolIcon && typeof toolIcon === 'object' && 'content' in toolIcon) {
|
|
||||||
icon = (
|
|
||||||
<AppIcon
|
|
||||||
className="!h-6 !w-6 rounded-lg border-[0.5px] border-white/2 shadow-md"
|
|
||||||
size="tiny"
|
|
||||||
icon={toolIcon.content}
|
|
||||||
background={toolIcon.background}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
icon = (
|
|
||||||
<div className="bg-util-colors-white-white-500 flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 text-white shadow-md">
|
|
||||||
<span className="text-xs font-medium text-text-tertiary">P</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
allTriggers.push({
|
allTriggers.push({
|
||||||
id: node.id,
|
id: node.id,
|
||||||
@ -139,9 +108,10 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
|
|||||||
type: 'user_input',
|
type: 'user_input',
|
||||||
name: (startNode.data as CommonNodeType)?.title || t('workflow.blocks.start'),
|
name: (startNode.data as CommonNodeType)?.title || t('workflow.blocks.start'),
|
||||||
icon: (
|
icon: (
|
||||||
<div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-blue-brand-blue-brand-500 text-white shadow-md">
|
<BlockIcon
|
||||||
<Home className="h-3.5 w-3.5" />
|
type={BlockEnum.Start}
|
||||||
</div>
|
size='md'
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
nodeId: startNode.id,
|
nodeId: startNode.id,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@ -166,5 +136,5 @@ export const useDynamicTestRunOptions = (): TestRunOptions => {
|
|||||||
triggers: allTriggers,
|
triggers: allTriggers,
|
||||||
runAll,
|
runAll,
|
||||||
}
|
}
|
||||||
}, [nodes, buildInTools, customTools, workflowTools, mcpTools, getIconUrl, t])
|
}, [nodes, buildInTools, customTools, workflowTools, mcpTools, triggerPlugins, t])
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user