mirror of
https://github.com/langgenius/dify.git
synced 2026-01-27 06:02:10 +08:00
This comprehensive restore includes: ## Test Run System Restoration - Restore test-run-menu.tsx component with multi-trigger support and keyboard shortcuts - Restore use-dynamic-test-run-options.tsx hook for dynamic trigger option generation - Restore workflow-entry.ts utilities for entry node detection and validation - Integrate complete test run functionality back into run-mode.tsx ## Block Selector System Fixes - Fix workflow block selector constants by uncommenting BLOCKS and START_BLOCKS arrays - Restore proper i18n translations for trigger node descriptions using workflow.blocksAbout keys - Filter trigger types from Blocks tab to prevent duplication with Start tab - Fix trigger node handle display to match start node behavior (hide left input handles) ## Workflow Validation System Improvements - Restore unified workflow validation using correct getValidTreeNodes(nodes, edges) signature - Remove duplicate Start node validation from isRequired mechanism - Eliminate "user input must be added" validation error by setting Start node isRequired: false - Fix end node connectivity validation to properly detect valid workflow chains ## Component Integration - Verify all dependencies exist (TriggerAll icon, useAllTriggerPlugins hook) - Maintain keyboard shortcut integration (Alt+R, ~, 0-9 keys) - Preserve portal-based dropdown positioning and tooltip structure - Support multiple trigger types: user_input, schedule, webhook, plugin, all This restores the complete test run functionality that was missing from feat/trigger branch by systematically analyzing and restoring components from feat/trigger-backup-before-merge.
128 lines
4.6 KiB
TypeScript
128 lines
4.6 KiB
TypeScript
import React, { useCallback, useEffect, useRef } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { useWorkflowRun, useWorkflowRunValidation, useWorkflowStartRun } from '@/app/components/workflow/hooks'
|
|
import { useStore } from '@/app/components/workflow/store'
|
|
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
|
import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types'
|
|
import { getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils'
|
|
import cn from '@/utils/classnames'
|
|
import { RiLoader2Line, RiPlayLargeLine } from '@remixicon/react'
|
|
import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
|
import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options'
|
|
import TestRunMenu, { type TestRunMenuRef, type TriggerOption } from './test-run-menu'
|
|
|
|
type RunModeProps = {
|
|
text?: string
|
|
}
|
|
|
|
const RunMode = ({
|
|
text,
|
|
}: RunModeProps) => {
|
|
const { t } = useTranslation()
|
|
const { handleWorkflowStartRunInWorkflow } = useWorkflowStartRun()
|
|
const { handleStopRun } = useWorkflowRun()
|
|
const { validateBeforeRun } = useWorkflowRunValidation()
|
|
const workflowRunningData = useStore(s => s.workflowRunningData)
|
|
|
|
const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running
|
|
|
|
const dynamicOptions = useDynamicTestRunOptions()
|
|
const testRunMenuRef = useRef<TestRunMenuRef>(null)
|
|
|
|
useEffect(() => {
|
|
// @ts-expect-error - Dynamic property for backward compatibility with keyboard shortcuts
|
|
window._toggleTestRunDropdown = () => {
|
|
testRunMenuRef.current?.toggle()
|
|
}
|
|
return () => {
|
|
// @ts-expect-error - Dynamic property cleanup
|
|
delete window._toggleTestRunDropdown
|
|
}
|
|
}, [])
|
|
|
|
const handleStop = useCallback(() => {
|
|
handleStopRun(workflowRunningData?.task_id || '')
|
|
}, [handleStopRun, workflowRunningData?.task_id])
|
|
|
|
const handleTriggerSelect = useCallback((option: TriggerOption) => {
|
|
// Validate checklist before running any workflow
|
|
if (!validateBeforeRun())
|
|
return
|
|
|
|
if (option.type === 'user_input') {
|
|
handleWorkflowStartRunInWorkflow()
|
|
}
|
|
else {
|
|
// Placeholder for trigger-specific execution logic for schedule, webhook, plugin types
|
|
console.log('TODO: Handle trigger execution for type:', option.type, 'nodeId:', option.nodeId)
|
|
}
|
|
}, [validateBeforeRun, handleWorkflowStartRunInWorkflow])
|
|
|
|
const { eventEmitter } = useEventEmitterContextContext()
|
|
eventEmitter?.useSubscription((v: any) => {
|
|
if (v.type === EVENT_WORKFLOW_STOP)
|
|
handleStop()
|
|
})
|
|
|
|
return (
|
|
<div className='flex items-center gap-x-px'>
|
|
{
|
|
isRunning
|
|
? (
|
|
<button
|
|
type='button'
|
|
className={cn(
|
|
'system-xs-medium flex h-7 cursor-not-allowed items-center gap-x-1 rounded-l-md bg-state-accent-hover px-1.5 text-text-accent',
|
|
)}
|
|
disabled={true}
|
|
>
|
|
<RiLoader2Line className='mr-1 size-4 animate-spin' />
|
|
{t('workflow.common.running')}
|
|
</button>
|
|
)
|
|
: (
|
|
<TestRunMenu
|
|
ref={testRunMenuRef}
|
|
options={dynamicOptions}
|
|
onSelect={handleTriggerSelect}
|
|
>
|
|
<div
|
|
className={cn(
|
|
'system-xs-medium flex h-7 cursor-pointer items-center gap-x-1 rounded-md px-1.5 text-text-accent hover:bg-state-accent-hover',
|
|
)}
|
|
style={{ userSelect: 'none' }}
|
|
>
|
|
<RiPlayLargeLine className='mr-1 size-4' />
|
|
{text ?? t('workflow.common.run')}
|
|
<div className='system-kbd flex items-center gap-x-0.5 text-text-tertiary'>
|
|
<div className='flex size-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray'>
|
|
{getKeyboardKeyNameBySystem('alt')}
|
|
</div>
|
|
<div className='flex size-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray'>
|
|
R
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TestRunMenu>
|
|
)
|
|
}
|
|
{
|
|
isRunning && (
|
|
<button
|
|
type='button'
|
|
className={cn(
|
|
'flex size-7 items-center justify-center rounded-r-md bg-state-accent-active',
|
|
)}
|
|
onClick={handleStop}
|
|
>
|
|
<StopCircle className='size-4 text-text-accent' />
|
|
</button>
|
|
)
|
|
}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default React.memo(RunMode)
|