feat(workflow): add node position indicator and prevent event propagation
Add node position indicator to workflow run panel when not in publish mode. Also prevent event propagation when clicking the position indicator to avoid unintended panel toggling.pull/21659/head
parent
cea6522122
commit
091c480547
@ -1,267 +1,293 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
RiAlertFill,
|
RiAlertFill,
|
||||||
RiArrowRightSLine,
|
RiArrowRightSLine,
|
||||||
RiCheckboxCircleFill,
|
RiCheckboxCircleFill,
|
||||||
RiErrorWarningLine,
|
RiErrorWarningLine,
|
||||||
RiLoader2Line,
|
RiLoader2Line,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import BlockIcon from '../block-icon'
|
import BlockIcon from '../block-icon'
|
||||||
import { BlockEnum } from '../types'
|
import { BlockEnum } from '../types'
|
||||||
import { RetryLogTrigger } from './retry-log'
|
import { RetryLogTrigger } from './retry-log'
|
||||||
import { IterationLogTrigger } from './iteration-log'
|
import { IterationLogTrigger } from './iteration-log'
|
||||||
import { LoopLogTrigger } from './loop-log'
|
import { LoopLogTrigger } from './loop-log'
|
||||||
import { AgentLogTrigger } from './agent-log'
|
import { AgentLogTrigger } from './agent-log'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import StatusContainer from '@/app/components/workflow/run/status-container'
|
import StatusContainer from '@/app/components/workflow/run/status-container'
|
||||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||||
import type {
|
import type {
|
||||||
AgentLogItemWithChildren,
|
AgentLogItemWithChildren,
|
||||||
IterationDurationMap,
|
IterationDurationMap,
|
||||||
LoopDurationMap,
|
LoopDurationMap,
|
||||||
LoopVariableMap,
|
LoopVariableMap,
|
||||||
NodeTracing,
|
NodeTracing,
|
||||||
} from '@/types/workflow'
|
} from '@/types/workflow'
|
||||||
import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip'
|
import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip'
|
||||||
import { hasRetryNode } from '@/app/components/workflow/utils'
|
import { hasRetryNode } from '@/app/components/workflow/utils'
|
||||||
import { useDocLink } from '@/context/i18n'
|
import { useDocLink } from '@/context/i18n'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import NodePosition from '@/app/components/workflow/nodes/_base/components/node-position'
|
||||||
type Props = {
|
import type { XYPosition } from 'reactflow'
|
||||||
className?: string
|
import { WorkflowHistoryStoreContext } from '@/app/components/workflow/workflow-history-store'
|
||||||
nodeInfo: NodeTracing
|
|
||||||
allExecutions?: NodeTracing[]
|
type Props = {
|
||||||
inMessage?: boolean
|
className?: string
|
||||||
hideInfo?: boolean
|
nodeInfo: NodeTracing
|
||||||
hideProcessDetail?: boolean
|
allExecutions?: NodeTracing[]
|
||||||
onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void
|
inMessage?: boolean
|
||||||
onShowLoopDetail?: (detail: NodeTracing[][], loopDurationMap: LoopDurationMap, loopVariableMap: LoopVariableMap) => void
|
hideInfo?: boolean
|
||||||
onShowRetryDetail?: (detail: NodeTracing[]) => void
|
hideProcessDetail?: boolean
|
||||||
onShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
|
onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void
|
||||||
notShowIterationNav?: boolean
|
onShowLoopDetail?: (detail: NodeTracing[][], loopDurationMap: LoopDurationMap, loopVariableMap: LoopVariableMap) => void
|
||||||
notShowLoopNav?: boolean
|
onShowRetryDetail?: (detail: NodeTracing[]) => void
|
||||||
}
|
onShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void
|
||||||
|
notShowIterationNav?: boolean
|
||||||
const NodePanel: FC<Props> = ({
|
notShowLoopNav?: boolean
|
||||||
className,
|
}
|
||||||
nodeInfo,
|
|
||||||
allExecutions,
|
const NodePanel: FC<Props> = ({
|
||||||
inMessage = false,
|
className,
|
||||||
hideInfo = false,
|
nodeInfo,
|
||||||
hideProcessDetail,
|
allExecutions,
|
||||||
onShowIterationDetail,
|
inMessage = false,
|
||||||
onShowLoopDetail,
|
hideInfo = false,
|
||||||
onShowRetryDetail,
|
hideProcessDetail,
|
||||||
onShowAgentOrToolLog,
|
onShowIterationDetail,
|
||||||
notShowIterationNav,
|
onShowLoopDetail,
|
||||||
notShowLoopNav,
|
onShowRetryDetail,
|
||||||
}) => {
|
onShowAgentOrToolLog,
|
||||||
const [collapseState, doSetCollapseState] = useState<boolean>(true)
|
notShowIterationNav,
|
||||||
const setCollapseState = useCallback((state: boolean) => {
|
notShowLoopNav,
|
||||||
if (hideProcessDetail)
|
}) => {
|
||||||
return
|
const { store } = useContext(WorkflowHistoryStoreContext)
|
||||||
doSetCollapseState(state)
|
let inPublishMode = true
|
||||||
}, [hideProcessDetail])
|
let hasNode = false
|
||||||
const { t } = useTranslation()
|
let nodePosition: XYPosition = { x: 0, y: 0 }
|
||||||
const docLink = useDocLink()
|
let nodeWidth = 0
|
||||||
|
let nodeHeight = 0
|
||||||
const getTime = (time: number) => {
|
|
||||||
if (time < 1)
|
if (store) {
|
||||||
return `${(time * 1000).toFixed(3)} ms`
|
inPublishMode = false
|
||||||
if (time > 60)
|
const nodes = store.getState().nodes
|
||||||
return `${Number.parseInt(Math.round(time / 60).toString())} m ${(time % 60).toFixed(3)} s`
|
const currentNodeIndex = nodes.findIndex(node => node.id === nodeInfo.node_id)
|
||||||
return `${time.toFixed(3)} s`
|
const currentNode = nodes[currentNodeIndex]
|
||||||
}
|
nodePosition = currentNode?.position ?? { x: -1, y: -1 }
|
||||||
|
nodeWidth = currentNode?.width ?? -1
|
||||||
const getTokenCount = (tokens: number) => {
|
nodeHeight = currentNode?.height ?? -1
|
||||||
if (tokens < 1000)
|
hasNode = !!currentNode
|
||||||
return tokens
|
}
|
||||||
if (tokens >= 1000 && tokens < 1000000)
|
|
||||||
return `${Number.parseFloat((tokens / 1000).toFixed(3))}K`
|
const [collapseState, doSetCollapseState] = useState<boolean>(true)
|
||||||
if (tokens >= 1000000)
|
const setCollapseState = useCallback((state: boolean) => {
|
||||||
return `${Number.parseFloat((tokens / 1000000).toFixed(3))}M`
|
if (hideProcessDetail)
|
||||||
}
|
return
|
||||||
|
doSetCollapseState(state)
|
||||||
useEffect(() => {
|
}, [hideProcessDetail])
|
||||||
setCollapseState(!nodeInfo.expand)
|
const { t } = useTranslation()
|
||||||
}, [nodeInfo.expand, setCollapseState])
|
const docLink = useDocLink()
|
||||||
|
|
||||||
const isIterationNode = nodeInfo.node_type === BlockEnum.Iteration && !!nodeInfo.details?.length
|
const getTime = (time: number) => {
|
||||||
const isLoopNode = nodeInfo.node_type === BlockEnum.Loop && !!nodeInfo.details?.length
|
if (time < 1)
|
||||||
const isRetryNode = hasRetryNode(nodeInfo.node_type) && !!nodeInfo.retryDetail?.length
|
return `${(time * 1000).toFixed(3)} ms`
|
||||||
const isAgentNode = nodeInfo.node_type === BlockEnum.Agent && !!nodeInfo.agentLog?.length
|
if (time > 60)
|
||||||
const isToolNode = nodeInfo.node_type === BlockEnum.Tool && !!nodeInfo.agentLog?.length
|
return `${Number.parseInt(Math.round(time / 60).toString())} m ${(time % 60).toFixed(3)} s`
|
||||||
|
return `${time.toFixed(3)} s`
|
||||||
const inputsTitle = useMemo(() => {
|
}
|
||||||
let text = t('workflow.common.input')
|
|
||||||
if (nodeInfo.node_type === BlockEnum.Loop)
|
const getTokenCount = (tokens: number) => {
|
||||||
text = t('workflow.nodes.loop.initialLoopVariables')
|
if (tokens < 1000)
|
||||||
return text.toLocaleUpperCase()
|
return tokens
|
||||||
}, [nodeInfo.node_type, t])
|
if (tokens >= 1000 && tokens < 1000000)
|
||||||
const processDataTitle = t('workflow.common.processData').toLocaleUpperCase()
|
return `${Number.parseFloat((tokens / 1000).toFixed(3))}K`
|
||||||
const outputTitle = useMemo(() => {
|
if (tokens >= 1000000)
|
||||||
let text = t('workflow.common.output')
|
return `${Number.parseFloat((tokens / 1000000).toFixed(3))}M`
|
||||||
if (nodeInfo.node_type === BlockEnum.Loop)
|
}
|
||||||
text = t('workflow.nodes.loop.finalLoopVariables')
|
|
||||||
return text.toLocaleUpperCase()
|
useEffect(() => {
|
||||||
}, [nodeInfo.node_type, t])
|
setCollapseState(!nodeInfo.expand)
|
||||||
|
}, [nodeInfo.expand, setCollapseState])
|
||||||
return (
|
|
||||||
<div className={cn('px-2 py-1', className)}>
|
const isIterationNode = nodeInfo.node_type === BlockEnum.Iteration && !!nodeInfo.details?.length
|
||||||
<div className='group rounded-[10px] border border-components-panel-border bg-background-default shadow-xs transition-all hover:shadow-md'>
|
const isLoopNode = nodeInfo.node_type === BlockEnum.Loop && !!nodeInfo.details?.length
|
||||||
<div
|
const isRetryNode = hasRetryNode(nodeInfo.node_type) && !!nodeInfo.retryDetail?.length
|
||||||
className={cn(
|
const isAgentNode = nodeInfo.node_type === BlockEnum.Agent && !!nodeInfo.agentLog?.length
|
||||||
'flex cursor-pointer items-center pl-1 pr-3',
|
const isToolNode = nodeInfo.node_type === BlockEnum.Tool && !!nodeInfo.agentLog?.length
|
||||||
hideInfo ? 'py-2 pl-2' : 'py-1.5',
|
|
||||||
!collapseState && (hideInfo ? '!pb-1' : '!pb-1.5'),
|
const inputsTitle = useMemo(() => {
|
||||||
)}
|
let text = t('workflow.common.input')
|
||||||
onClick={() => setCollapseState(!collapseState)}
|
if (nodeInfo.node_type === BlockEnum.Loop)
|
||||||
>
|
text = t('workflow.nodes.loop.initialLoopVariables')
|
||||||
{!hideProcessDetail && (
|
return text.toLocaleUpperCase()
|
||||||
<RiArrowRightSLine
|
}, [nodeInfo.node_type, t])
|
||||||
className={cn(
|
const processDataTitle = t('workflow.common.processData').toLocaleUpperCase()
|
||||||
'mr-1 h-4 w-4 shrink-0 text-text-quaternary transition-all group-hover:text-text-tertiary',
|
const outputTitle = useMemo(() => {
|
||||||
!collapseState && 'rotate-90',
|
let text = t('workflow.common.output')
|
||||||
)}
|
if (nodeInfo.node_type === BlockEnum.Loop)
|
||||||
/>
|
text = t('workflow.nodes.loop.finalLoopVariables')
|
||||||
)}
|
return text.toLocaleUpperCase()
|
||||||
<BlockIcon size={inMessage ? 'xs' : 'sm'} className={cn('mr-2 shrink-0', inMessage && '!mr-1')} type={nodeInfo.node_type} toolIcon={nodeInfo.extras?.icon || nodeInfo.extras} />
|
}, [nodeInfo.node_type, t])
|
||||||
<Tooltip
|
|
||||||
popupContent={
|
return (
|
||||||
<div className='max-w-xs'>{nodeInfo.title}</div>
|
<div className={cn('px-2 py-1', className)}>
|
||||||
}
|
<div className='group rounded-[10px] border border-components-panel-border bg-background-default shadow-xs transition-all hover:shadow-md'>
|
||||||
>
|
<div
|
||||||
<div className={cn(
|
className={cn(
|
||||||
'system-xs-semibold-uppercase grow truncate text-text-secondary',
|
'flex cursor-pointer items-center pl-1 pr-3',
|
||||||
hideInfo && '!text-xs',
|
hideInfo ? 'py-2 pl-2' : 'py-1.5',
|
||||||
)}>{nodeInfo.title}</div>
|
!collapseState && (hideInfo ? '!pb-1' : '!pb-1.5'),
|
||||||
</Tooltip>
|
)}
|
||||||
{nodeInfo.status !== 'running' && !hideInfo && (
|
onClick={() => setCollapseState(!collapseState)}
|
||||||
<div className='system-xs-regular shrink-0 text-text-tertiary'>{nodeInfo.execution_metadata?.total_tokens ? `${getTokenCount(nodeInfo.execution_metadata?.total_tokens || 0)} tokens · ` : ''}{`${getTime(nodeInfo.elapsed_time || 0)}`}</div>
|
>
|
||||||
)}
|
{!hideProcessDetail && (
|
||||||
{nodeInfo.status === 'succeeded' && (
|
<RiArrowRightSLine
|
||||||
<RiCheckboxCircleFill className='ml-2 h-3.5 w-3.5 shrink-0 text-text-success' />
|
className={cn(
|
||||||
)}
|
'mr-1 h-4 w-4 shrink-0 text-text-quaternary transition-all group-hover:text-text-tertiary',
|
||||||
{nodeInfo.status === 'failed' && (
|
!collapseState && 'rotate-90',
|
||||||
<RiErrorWarningLine className='ml-2 h-3.5 w-3.5 shrink-0 text-text-warning' />
|
)}
|
||||||
)}
|
/>
|
||||||
{nodeInfo.status === 'stopped' && (
|
)}
|
||||||
<RiAlertFill className={cn('ml-2 h-4 w-4 shrink-0 text-text-warning-secondary', inMessage && 'h-3.5 w-3.5')} />
|
<BlockIcon size={inMessage ? 'xs' : 'sm'} className={cn('mr-2 shrink-0', inMessage && '!mr-1')} type={nodeInfo.node_type} toolIcon={nodeInfo.extras?.icon || nodeInfo.extras} />
|
||||||
)}
|
<Tooltip
|
||||||
{nodeInfo.status === 'exception' && (
|
popupContent={
|
||||||
<RiAlertFill className={cn('ml-2 h-4 w-4 shrink-0 text-text-warning-secondary', inMessage && 'h-3.5 w-3.5')} />
|
<div className='max-w-xs'>{nodeInfo.title}</div>
|
||||||
)}
|
}
|
||||||
{nodeInfo.status === 'running' && (
|
>
|
||||||
<div className='flex shrink-0 items-center text-[13px] font-medium leading-[16px] text-text-accent'>
|
<div className={cn(
|
||||||
<span className='mr-2 text-xs font-normal'>Running</span>
|
'system-xs-semibold-uppercase grow truncate text-text-secondary',
|
||||||
<RiLoader2Line className='h-3.5 w-3.5 animate-spin' />
|
hideInfo && '!text-xs',
|
||||||
</div>
|
)}>{nodeInfo.title}</div>
|
||||||
)}
|
</Tooltip>
|
||||||
</div>
|
{nodeInfo.status !== 'running' && !hideInfo && (
|
||||||
{!collapseState && !hideProcessDetail && (
|
<div className='system-xs-regular shrink-0 text-text-tertiary'>{nodeInfo.execution_metadata?.total_tokens ? `${getTokenCount(nodeInfo.execution_metadata?.total_tokens || 0)} tokens · ` : ''}{`${getTime(nodeInfo.elapsed_time || 0)}`}</div>
|
||||||
<div className='px-1 pb-1'>
|
)}
|
||||||
{/* The nav to the iteration detail */}
|
{nodeInfo.status === 'succeeded' && (
|
||||||
{isIterationNode && !notShowIterationNav && onShowIterationDetail && (
|
<RiCheckboxCircleFill className='ml-2 h-3.5 w-3.5 shrink-0 text-text-success' />
|
||||||
<IterationLogTrigger
|
)}
|
||||||
nodeInfo={nodeInfo}
|
{nodeInfo.status === 'failed' && (
|
||||||
allExecutions={allExecutions}
|
<RiErrorWarningLine className='ml-2 h-3.5 w-3.5 shrink-0 text-text-warning' />
|
||||||
onShowIterationResultList={onShowIterationDetail}
|
)}
|
||||||
/>
|
{nodeInfo.status === 'stopped' && (
|
||||||
)}
|
<RiAlertFill className={cn('ml-2 h-4 w-4 shrink-0 text-text-warning-secondary', inMessage && 'h-3.5 w-3.5')} />
|
||||||
{/* The nav to the Loop detail */}
|
)}
|
||||||
{isLoopNode && !notShowLoopNav && onShowLoopDetail && (
|
{nodeInfo.status === 'exception' && (
|
||||||
<LoopLogTrigger
|
<RiAlertFill className={cn('ml-2 h-4 w-4 shrink-0 text-text-warning-secondary', inMessage && 'h-3.5 w-3.5')} />
|
||||||
nodeInfo={nodeInfo}
|
)}
|
||||||
allExecutions={allExecutions}
|
{nodeInfo.status === 'running' && (
|
||||||
onShowLoopResultList={onShowLoopDetail}
|
<div className='flex shrink-0 items-center text-[13px] font-medium leading-[16px] text-text-accent'>
|
||||||
/>
|
<span className='mr-2 text-xs font-normal'>Running</span>
|
||||||
)}
|
<RiLoader2Line className='h-3.5 w-3.5 animate-spin' />
|
||||||
{isRetryNode && onShowRetryDetail && (
|
</div>
|
||||||
<RetryLogTrigger
|
)}
|
||||||
nodeInfo={nodeInfo}
|
{!inPublishMode && (
|
||||||
onShowRetryResultList={onShowRetryDetail}
|
<div className='ml-1' style={{ pointerEvents: hasNode ? 'auto' : 'none', opacity: hasNode ? 1 : 0.5 }}>
|
||||||
/>
|
<NodePosition nodePosition={nodePosition} nodeWidth={nodeWidth} nodeHeight={nodeHeight}></NodePosition>
|
||||||
)}
|
</div>
|
||||||
{
|
)}
|
||||||
(isAgentNode || isToolNode) && onShowAgentOrToolLog && (
|
</div>
|
||||||
<AgentLogTrigger
|
{!collapseState && !hideProcessDetail && (
|
||||||
nodeInfo={nodeInfo}
|
<div className='px-1 pb-1'>
|
||||||
onShowAgentOrToolLog={onShowAgentOrToolLog}
|
{/* The nav to the iteration detail */}
|
||||||
/>
|
{isIterationNode && !notShowIterationNav && onShowIterationDetail && (
|
||||||
)
|
<IterationLogTrigger
|
||||||
}
|
nodeInfo={nodeInfo}
|
||||||
<div className={cn('mb-1', hideInfo && '!px-2 !py-0.5')}>
|
allExecutions={allExecutions}
|
||||||
{(nodeInfo.status === 'stopped') && (
|
onShowIterationResultList={onShowIterationDetail}
|
||||||
<StatusContainer status='stopped'>
|
/>
|
||||||
{t('workflow.tracing.stopBy', { user: nodeInfo.created_by ? nodeInfo.created_by.name : 'N/A' })}
|
)}
|
||||||
</StatusContainer>
|
{/* The nav to the Loop detail */}
|
||||||
)}
|
{isLoopNode && !notShowLoopNav && onShowLoopDetail && (
|
||||||
{(nodeInfo.status === 'exception') && (
|
<LoopLogTrigger
|
||||||
<StatusContainer status='stopped'>
|
nodeInfo={nodeInfo}
|
||||||
{nodeInfo.error}
|
allExecutions={allExecutions}
|
||||||
<a
|
onShowLoopResultList={onShowLoopDetail}
|
||||||
href={docLink('/guides/workflow/error-handling/error-type')}
|
/>
|
||||||
target='_blank'
|
)}
|
||||||
className='text-text-accent'
|
{isRetryNode && onShowRetryDetail && (
|
||||||
>
|
<RetryLogTrigger
|
||||||
{t('workflow.common.learnMore')}
|
nodeInfo={nodeInfo}
|
||||||
</a>
|
onShowRetryResultList={onShowRetryDetail}
|
||||||
</StatusContainer>
|
/>
|
||||||
)}
|
)}
|
||||||
{nodeInfo.status === 'failed' && (
|
{
|
||||||
<StatusContainer status='failed'>
|
(isAgentNode || isToolNode) && onShowAgentOrToolLog && (
|
||||||
{nodeInfo.error}
|
<AgentLogTrigger
|
||||||
</StatusContainer>
|
nodeInfo={nodeInfo}
|
||||||
)}
|
onShowAgentOrToolLog={onShowAgentOrToolLog}
|
||||||
{nodeInfo.status === 'retry' && (
|
/>
|
||||||
<StatusContainer status='failed'>
|
)
|
||||||
{nodeInfo.error}
|
}
|
||||||
</StatusContainer>
|
<div className={cn('mb-1', hideInfo && '!px-2 !py-0.5')}>
|
||||||
)}
|
{(nodeInfo.status === 'stopped') && (
|
||||||
</div>
|
<StatusContainer status='stopped'>
|
||||||
{nodeInfo.inputs && (
|
{t('workflow.tracing.stopBy', { user: nodeInfo.created_by ? nodeInfo.created_by.name : 'N/A' })}
|
||||||
<div className={cn('mb-1')}>
|
</StatusContainer>
|
||||||
<CodeEditor
|
)}
|
||||||
readOnly
|
{(nodeInfo.status === 'exception') && (
|
||||||
title={<div>{inputsTitle}</div>}
|
<StatusContainer status='stopped'>
|
||||||
language={CodeLanguage.json}
|
{nodeInfo.error}
|
||||||
value={nodeInfo.inputs}
|
<a
|
||||||
isJSONStringifyBeauty
|
href={docLink('/guides/workflow/error-handling/error-type')}
|
||||||
/>
|
target='_blank'
|
||||||
</div>
|
className='text-text-accent'
|
||||||
)}
|
>
|
||||||
{nodeInfo.process_data && (
|
{t('workflow.common.learnMore')}
|
||||||
<div className={cn('mb-1')}>
|
</a>
|
||||||
<CodeEditor
|
</StatusContainer>
|
||||||
readOnly
|
)}
|
||||||
title={<div>{processDataTitle}</div>}
|
{nodeInfo.status === 'failed' && (
|
||||||
language={CodeLanguage.json}
|
<StatusContainer status='failed'>
|
||||||
value={nodeInfo.process_data}
|
{nodeInfo.error}
|
||||||
isJSONStringifyBeauty
|
</StatusContainer>
|
||||||
/>
|
)}
|
||||||
</div>
|
{nodeInfo.status === 'retry' && (
|
||||||
)}
|
<StatusContainer status='failed'>
|
||||||
{nodeInfo.outputs && (
|
{nodeInfo.error}
|
||||||
<div>
|
</StatusContainer>
|
||||||
<CodeEditor
|
)}
|
||||||
readOnly
|
</div>
|
||||||
title={<div>{outputTitle}</div>}
|
{nodeInfo.inputs && (
|
||||||
language={CodeLanguage.json}
|
<div className={cn('mb-1')}>
|
||||||
value={nodeInfo.outputs}
|
<CodeEditor
|
||||||
isJSONStringifyBeauty
|
readOnly
|
||||||
tip={<ErrorHandleTip type={nodeInfo.execution_metadata?.error_strategy} />}
|
title={<div>{inputsTitle}</div>}
|
||||||
/>
|
language={CodeLanguage.json}
|
||||||
</div>
|
value={nodeInfo.inputs}
|
||||||
)}
|
isJSONStringifyBeauty
|
||||||
</div>
|
/>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
{nodeInfo.process_data && (
|
||||||
)
|
<div className={cn('mb-1')}>
|
||||||
}
|
<CodeEditor
|
||||||
|
readOnly
|
||||||
export default NodePanel
|
title={<div>{processDataTitle}</div>}
|
||||||
|
language={CodeLanguage.json}
|
||||||
|
value={nodeInfo.process_data}
|
||||||
|
isJSONStringifyBeauty
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{nodeInfo.outputs && (
|
||||||
|
<div>
|
||||||
|
<CodeEditor
|
||||||
|
readOnly
|
||||||
|
title={<div>{outputTitle}</div>}
|
||||||
|
language={CodeLanguage.json}
|
||||||
|
value={nodeInfo.outputs}
|
||||||
|
isJSONStringifyBeauty
|
||||||
|
tip={<ErrorHandleTip type={nodeInfo.execution_metadata?.error_strategy} />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NodePanel
|
||||||
|
|||||||
Loading…
Reference in New Issue