mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-09 10:56:52 +08:00
feat: workflow remove preview mode (#3941)
This commit is contained in:
@@ -5,18 +5,25 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useStore } from '../../store'
|
||||
import {
|
||||
useStore,
|
||||
useWorkflowStore,
|
||||
} from '../../store'
|
||||
import { useWorkflowRun } from '../../hooks'
|
||||
import UserInput from './user-input'
|
||||
import Chat from '@/app/components/base/chat/chat'
|
||||
import type { ChatItem } from '@/app/components/base/chat/types'
|
||||
import { fetchConvesationMessages } from '@/service/debug'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
const ChatRecord = () => {
|
||||
const [fetched, setFetched] = useState(false)
|
||||
const [chatList, setChatList] = useState([])
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { handleLoadBackupDraft } = useWorkflowRun()
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
const currentConversationID = historyWorkflowData?.conversation_id
|
||||
|
||||
@@ -79,6 +86,15 @@ const ChatRecord = () => {
|
||||
<>
|
||||
<div className='shrink-0 flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'>
|
||||
{`TEST CHAT#${historyWorkflowData?.sequence_number}`}
|
||||
<div
|
||||
className='flex justify-center items-center w-6 h-6 cursor-pointer'
|
||||
onClick={() => {
|
||||
handleLoadBackupDraft()
|
||||
workflowStore.setState({ historyWorkflowData: undefined })
|
||||
}}
|
||||
>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='grow h-0'>
|
||||
<Chat
|
||||
|
||||
@@ -3,10 +3,17 @@ import {
|
||||
useRef,
|
||||
} from 'react'
|
||||
import { useKeyPress } from 'ahooks'
|
||||
import cn from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
useEdgesInteractions,
|
||||
useNodesInteractions,
|
||||
useWorkflowInteractions,
|
||||
} from '../../hooks'
|
||||
import ChatWrapper from './chat-wrapper'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
export type ChatWrapperRefType = {
|
||||
handleRestart: () => void
|
||||
@@ -14,33 +21,56 @@ export type ChatWrapperRefType = {
|
||||
const DebugAndPreview = () => {
|
||||
const { t } = useTranslation()
|
||||
const chatRef = useRef({ handleRestart: () => {} })
|
||||
const { handleCancelDebugAndPreviewPanel } = useWorkflowInteractions()
|
||||
const { handleNodeCancelRunningStatus } = useNodesInteractions()
|
||||
const { handleEdgeCancelRunningStatus } = useEdgesInteractions()
|
||||
|
||||
const handleRestartChat = () => {
|
||||
handleNodeCancelRunningStatus()
|
||||
handleEdgeCancelRunningStatus()
|
||||
chatRef.current.handleRestart()
|
||||
}
|
||||
|
||||
useKeyPress('shift.r', () => {
|
||||
chatRef.current.handleRestart()
|
||||
handleRestartChat()
|
||||
}, {
|
||||
exactMatch: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
flex flex-col w-[400px] rounded-l-2xl h-full border border-black/[0.02] shadow-xl
|
||||
`}
|
||||
className={cn(
|
||||
'flex flex-col w-[400px] rounded-l-2xl h-full border border-black/[0.02]',
|
||||
)}
|
||||
style={{
|
||||
background: 'linear-gradient(156deg, rgba(242, 244, 247, 0.80) 0%, rgba(242, 244, 247, 0.00) 99.43%), var(--white, #FFF)',
|
||||
}}
|
||||
>
|
||||
<div className='shrink-0 flex items-center justify-between px-4 pt-3 pb-2 font-semibold text-gray-900'>
|
||||
<div className='shrink-0 flex items-center justify-between pl-4 pr-3 pt-3 pb-2 font-semibold text-gray-900'>
|
||||
{t('workflow.common.debugAndPreview').toLocaleUpperCase()}
|
||||
<Button
|
||||
className='pl-2.5 pr-[7px] h-8 bg-white border-[0.5px] border-gray-200 shadow-xs rounded-lg text-[13px] text-primary-600 font-semibold'
|
||||
onClick={() => chatRef.current.handleRestart()}
|
||||
>
|
||||
<RefreshCcw01 className='mr-1 w-3.5 h-3.5' />
|
||||
{t('common.operation.refresh')}
|
||||
<div className='ml-2 px-1 leading-[18px] rounded-md border border-gray-200 bg-gray-50 text-[11px] text-gray-500 font-medium'>Shift</div>
|
||||
<div className='ml-0.5 px-1 leading-[18px] rounded-md border border-gray-200 bg-gray-50 text-[11px] text-gray-500 font-medium'>R</div>
|
||||
</Button>
|
||||
<div className='flex items-center'>
|
||||
<Button
|
||||
className='px-2 h-8 bg-white border-[0.5px] border-gray-200 shadow-xs rounded-lg text-xs text-gray-700 font-medium'
|
||||
onClick={() => handleRestartChat()}
|
||||
>
|
||||
<RefreshCcw01 className='shrink-0 mr-1 w-3 h-3 text-gray-500' />
|
||||
<div
|
||||
className='grow truncate uppercase'
|
||||
title={t('common.operation.refresh') || ''}
|
||||
>
|
||||
{t('common.operation.refresh')}
|
||||
</div>
|
||||
<div className='shrink-0 ml-1 px-1 leading-[18px] rounded-md border border-gray-200 bg-gray-50 text-[11px] text-gray-500 font-medium'>Shift</div>
|
||||
<div className='shrink-0 ml-0.5 px-1 leading-[18px] rounded-md border border-gray-200 bg-gray-50 text-[11px] text-gray-500 font-medium'>R</div>
|
||||
</Button>
|
||||
<div className='mx-3 w-[1px] h-3.5 bg-gray-200'></div>
|
||||
<div
|
||||
className='flex items-center justify-center w-6 h-6 cursor-pointer'
|
||||
onClick={handleCancelDebugAndPreviewPanel}
|
||||
>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='grow rounded-b-2xl overflow-y-auto'>
|
||||
<ChatWrapper ref={chatRef} />
|
||||
|
||||
@@ -56,12 +56,13 @@ const UserInput = () => {
|
||||
expanded && (
|
||||
<div className='py-2 text-[13px] text-gray-900'>
|
||||
{
|
||||
variables.map(variable => (
|
||||
variables.map((variable, index) => (
|
||||
<div
|
||||
key={variable.variable}
|
||||
className='mb-2 last-of-type:mb-0'
|
||||
>
|
||||
<FormItem
|
||||
autoFocus={index === 0}
|
||||
payload={variable}
|
||||
value={inputs[variable.variable]}
|
||||
onChange={v => handleValueChange(variable.variable, v)}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import type { FC } from 'react'
|
||||
import {
|
||||
memo,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import { memo } from 'react'
|
||||
import { useNodes } from 'reactflow'
|
||||
import cn from 'classnames'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import type { CommonNodeType } from '../types'
|
||||
import { Panel as NodePanel } from '../nodes'
|
||||
@@ -23,9 +21,8 @@ const Panel: FC = () => {
|
||||
const nodes = useNodes<CommonNodeType>()
|
||||
const isChatMode = useIsChatMode()
|
||||
const selectedNode = nodes.find(node => node.data.selected)
|
||||
const showInputsPanel = useStore(s => s.showInputsPanel)
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel)
|
||||
const isRestoring = useStore(s => s.isRestoring)
|
||||
const {
|
||||
enableShortcuts,
|
||||
@@ -37,28 +34,13 @@ const Panel: FC = () => {
|
||||
showMessageLogModal: state.showMessageLogModal,
|
||||
setShowMessageLogModal: state.setShowMessageLogModal,
|
||||
})))
|
||||
const {
|
||||
showNodePanel,
|
||||
showDebugAndPreviewPanel,
|
||||
showWorkflowPreview,
|
||||
} = useMemo(() => {
|
||||
return {
|
||||
showNodePanel: !!selectedNode && !workflowRunningData && !historyWorkflowData && !showInputsPanel,
|
||||
showDebugAndPreviewPanel: isChatMode && workflowRunningData && !historyWorkflowData,
|
||||
showWorkflowPreview: !isChatMode && !historyWorkflowData && (workflowRunningData || showInputsPanel),
|
||||
}
|
||||
}, [
|
||||
showInputsPanel,
|
||||
selectedNode,
|
||||
isChatMode,
|
||||
workflowRunningData,
|
||||
historyWorkflowData,
|
||||
])
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className='absolute top-14 right-0 bottom-2 flex z-10 outline-none'
|
||||
className={cn(
|
||||
'absolute top-14 right-0 bottom-2 flex z-10 outline-none',
|
||||
)}
|
||||
onFocus={disableShortcuts}
|
||||
onBlur={enableShortcuts}
|
||||
key={`${isRestoring}`}
|
||||
@@ -76,6 +58,11 @@ const Panel: FC = () => {
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
!!selectedNode && (
|
||||
<NodePanel {...selectedNode!} />
|
||||
)
|
||||
}
|
||||
{
|
||||
historyWorkflowData && !isChatMode && (
|
||||
<Record />
|
||||
@@ -87,20 +74,15 @@ const Panel: FC = () => {
|
||||
)
|
||||
}
|
||||
{
|
||||
showDebugAndPreviewPanel && (
|
||||
showDebugAndPreviewPanel && isChatMode && (
|
||||
<DebugAndPreview />
|
||||
)
|
||||
}
|
||||
{
|
||||
showWorkflowPreview && (
|
||||
showDebugAndPreviewPanel && !isChatMode && (
|
||||
<WorkflowPreview />
|
||||
)
|
||||
}
|
||||
{
|
||||
showNodePanel && (
|
||||
<NodePanel {...selectedNode!} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ const InputsPanel = ({ onRun }: Props) => {
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
const {
|
||||
handleRun,
|
||||
handleRunSetting,
|
||||
} = useWorkflowRun()
|
||||
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
|
||||
const startVariables = startNode?.data.variables
|
||||
@@ -72,7 +71,6 @@ const InputsPanel = ({ onRun }: Props) => {
|
||||
|
||||
const doRun = () => {
|
||||
onRun()
|
||||
handleRunSetting()
|
||||
handleRun({ inputs, files })
|
||||
}
|
||||
|
||||
@@ -87,12 +85,13 @@ const InputsPanel = ({ onRun }: Props) => {
|
||||
<>
|
||||
<div className='px-4 pb-2'>
|
||||
{
|
||||
variables.map(variable => (
|
||||
variables.map((variable, index) => (
|
||||
<div
|
||||
key={variable.variable}
|
||||
className='mb-2 last-of-type:mb-0'
|
||||
>
|
||||
<FormItem
|
||||
autoFocus={index === 0}
|
||||
className='!block'
|
||||
payload={variable}
|
||||
value={inputs[variable.variable]}
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
import { memo } from 'react'
|
||||
import { memo, useCallback } from 'react'
|
||||
import type { WorkflowDataUpdator } from '../types'
|
||||
import Run from '../run'
|
||||
import { useStore } from '../store'
|
||||
import { useWorkflowInteractions } from '../hooks'
|
||||
|
||||
const Record = () => {
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
const { handleUpdateWorkflowCanvas } = useWorkflowInteractions()
|
||||
|
||||
const handleResultCallback = useCallback((res: any) => {
|
||||
const graph: WorkflowDataUpdator = res.graph
|
||||
handleUpdateWorkflowCanvas({
|
||||
nodes: graph.nodes,
|
||||
edges: graph.edges,
|
||||
viewport: graph.viewport,
|
||||
})
|
||||
}, [handleUpdateWorkflowCanvas])
|
||||
|
||||
return (
|
||||
<div className='flex flex-col w-[400px] h-full rounded-l-2xl border-[0.5px] border-gray-200 shadow-xl bg-white'>
|
||||
<div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'>
|
||||
{`Test Run#${historyWorkflowData?.sequence_number}`}
|
||||
</div>
|
||||
<Run runID={historyWorkflowData?.id || ''} />
|
||||
<Run
|
||||
runID={historyWorkflowData?.id || ''}
|
||||
getResultCallback={handleResultCallback}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import OutputPanel from '../run/output-panel'
|
||||
import ResultPanel from '../run/result-panel'
|
||||
import TracingPanel from '../run/tracing-panel'
|
||||
import {
|
||||
useWorkflowRun,
|
||||
useWorkflowInteractions,
|
||||
} from '../hooks'
|
||||
import { useStore } from '../store'
|
||||
import {
|
||||
@@ -22,9 +22,10 @@ import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
const WorkflowPreview = () => {
|
||||
const { t } = useTranslation()
|
||||
const { handleRunSetting } = useWorkflowRun()
|
||||
const showInputsPanel = useStore(s => s.showInputsPanel)
|
||||
const { handleCancelDebugAndPreviewPanel } = useWorkflowInteractions()
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
const showInputsPanel = useStore(s => s.showInputsPanel)
|
||||
const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel)
|
||||
const [currentTab, setCurrentTab] = useState<string>(showInputsPanel ? 'INPUT' : 'TRACING')
|
||||
|
||||
const switchTab = async (tab: string) => {
|
||||
@@ -34,6 +35,11 @@ const WorkflowPreview = () => {
|
||||
const [height, setHieght] = useState(0)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (showDebugAndPreviewPanel && showInputsPanel)
|
||||
setCurrentTab('INPUT')
|
||||
}, [showDebugAndPreviewPanel, showInputsPanel])
|
||||
|
||||
const adjustResultHeight = () => {
|
||||
if (ref.current)
|
||||
setHieght(ref.current?.clientHeight - 16 - 16 - 2 - 1)
|
||||
@@ -49,11 +55,9 @@ const WorkflowPreview = () => {
|
||||
`}>
|
||||
<div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'>
|
||||
{`Test Run${!workflowRunningData?.result.sequence_number ? '' : `#${workflowRunningData?.result.sequence_number}`}`}
|
||||
{showInputsPanel && workflowRunningData?.result?.status !== WorkflowRunningStatus.Running && (
|
||||
<div className='p-1 cursor-pointer' onClick={() => handleRunSetting(true)}>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
)}
|
||||
<div className='p-1 cursor-pointer' onClick={() => handleCancelDebugAndPreviewPanel()}>
|
||||
<XClose className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='grow relative flex flex-col'>
|
||||
<div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
|
||||
@@ -107,7 +111,7 @@ const WorkflowPreview = () => {
|
||||
'grow bg-white h-0 overflow-y-auto rounded-b-2xl',
|
||||
(currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-gray-50',
|
||||
)}>
|
||||
{currentTab === 'INPUT' && (
|
||||
{currentTab === 'INPUT' && showInputsPanel && (
|
||||
<InputsPanel onRun={() => switchTab('RESULT')} />
|
||||
)}
|
||||
{currentTab === 'RESULT' && (
|
||||
|
||||
Reference in New Issue
Block a user