import type { HumanInputFormSubmitData } from '@/app/components/base/chat/chat/answer/human-input-content/type' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { toast } from '@langgenius/dify-ui/toast' import copy from 'copy-to-clipboard' import { memo, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import ReasoningPanel from '@/app/components/base/chat/chat/answer/reasoning-panel' import Loading from '@/app/components/base/loading' import { submitHumanInputForm } from '@/service/workflow' import { useWorkflowInteractions } from '../hooks' import ResultPanel from '../run/result-panel' import ResultText from '../run/result-text' import TracingPanel from '../run/tracing-panel' import { useStore } from '../store' import { WorkflowRunningStatus } from '../types' import { formatWorkflowRunIdentifier } from '../utils' import HumanInputFilledFormList from './human-input-filled-form-list' import HumanInputFormList from './human-input-form-list' import InputsPanel from './inputs-panel' const WorkflowPreview = () => { const { t } = useTranslation() const { handleCancelDebugAndPreviewPanel } = useWorkflowInteractions() const workflowRunningData = useStore((s) => s.workflowRunningData) const isListening = useStore((s) => s.isListening) const showInputsPanel = useStore((s) => s.showInputsPanel) const workflowCanvasWidth = useStore((s) => s.workflowCanvasWidth) const panelWidth = useStore((s) => s.previewPanelWidth) const setPreviewPanelWidth = useStore((s) => s.setPreviewPanelWidth) const showDebugAndPreviewPanel = useStore((s) => s.showDebugAndPreviewPanel) const humanInputFormDataList = useStore((s) => s.workflowRunningData?.humanInputFormDataList) const humanInputFilledFormDataList = useStore( (s) => s.workflowRunningData?.humanInputFilledFormDataList, ) const [currentTab, setCurrentTab] = useState(showInputsPanel ? 'INPUT' : 'TRACING') const switchTab = async (tab: string) => { setCurrentTab(tab) } useEffect(() => { if (showDebugAndPreviewPanel && showInputsPanel) switchTab('INPUT') }, [showDebugAndPreviewPanel, showInputsPanel]) useEffect(() => { if (isListening) switchTab('DETAIL') }, [isListening]) useEffect(() => { const status = workflowRunningData?.result.status if (!workflowRunningData) return if ( (status === WorkflowRunningStatus.Succeeded || status === WorkflowRunningStatus.Failed) && !workflowRunningData.resultText && !workflowRunningData.result.files?.length ) switchTab('DETAIL') if (status === WorkflowRunningStatus.Paused) switchTab('RESULT') }, [workflowRunningData]) const [isResizing, setIsResizing] = useState(false) const startResizing = useCallback((e: React.MouseEvent) => { e.preventDefault() setIsResizing(true) }, []) const stopResizing = useCallback(() => { setIsResizing(false) }, []) const resize = useCallback( (e: MouseEvent) => { if (isResizing) { const newWidth = window.innerWidth - e.clientX // width constraints: 400 <= width <= maxAllowed (canvas - reserved 400) const reservedCanvasWidth = 400 const maxAllowed = workflowCanvasWidth ? workflowCanvasWidth - reservedCanvasWidth : 1024 if (newWidth >= 400 && newWidth <= maxAllowed) setPreviewPanelWidth(newWidth) } }, [isResizing, workflowCanvasWidth, setPreviewPanelWidth], ) useEffect(() => { window.addEventListener('mousemove', resize) window.addEventListener('mouseup', stopResizing) return () => { window.removeEventListener('mousemove', resize) window.removeEventListener('mouseup', stopResizing) } }, [resize, stopResizing]) const handleSubmitHumanInputForm = useCallback( async (formToken: string, formData: HumanInputFormSubmitData) => { await submitHumanInputForm(formToken, formData) }, [], ) const handleOpenTracingTab = useCallback(() => { switchTab('TRACING') }, []) return (
{`Test Run${formatWorkflowRunIdentifier(workflowRunningData?.result.finished_at, workflowRunningData?.result.status)}`}
{showInputsPanel && (
switchTab('INPUT')} > {t(($) => $.input, { ns: 'runLog' })}
)}
{ if (!workflowRunningData) return switchTab('RESULT') }} > {t(($) => $.result, { ns: 'runLog' })}
{ if (!workflowRunningData) return switchTab('DETAIL') }} > {t(($) => $.detail, { ns: 'runLog' })}
{ if (!workflowRunningData) return switchTab('TRACING') }} > {t(($) => $.tracing, { ns: 'runLog' })}
{currentTab === 'INPUT' && showInputsPanel && ( switchTab('RESULT')} /> )} {currentTab === 'RESULT' && (
{humanInputFormDataList && humanInputFormDataList.length > 0 && ( )} {humanInputFilledFormDataList && humanInputFilledFormDataList.length > 0 && ( )} {workflowRunningData?.reasoningContent && Object.values(workflowRunningData.reasoningContent).some(Boolean) && ( )} switchTab('DETAIL')} /> {workflowRunningData?.result.status === WorkflowRunningStatus.Succeeded && workflowRunningData?.resultText && typeof workflowRunningData?.resultText === 'string' && ( )}
)} {currentTab === 'DETAIL' && ( )} {currentTab === 'DETAIL' && !workflowRunningData?.result && (
)} {currentTab === 'TRACING' && ( )} {currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && (
)}
) } export default memo(WorkflowPreview)