mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 18:58:35 +08:00
245 lines
7.8 KiB
TypeScript
245 lines
7.8 KiB
TypeScript
import type { FC } from 'react'
|
|
import type { NodeProps } from '../types'
|
|
import type { VarInInspect } from '@/types/workflow'
|
|
import { cn } from '@langgenius/dify-ui/cn'
|
|
import { RiCloseLine } from '@remixicon/react'
|
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import ActionButton from '@/app/components/base/action-button'
|
|
import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types'
|
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
|
import { VarInInspectType } from '@/types/workflow'
|
|
import useCurrentVars from '../hooks/use-inspect-vars-crud'
|
|
import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type'
|
|
import { useStore } from '../store'
|
|
import Empty from './empty'
|
|
import Left from './left'
|
|
import Listening from './listening'
|
|
import Right from './right'
|
|
|
|
export type currentVarType = {
|
|
nodeId: string
|
|
nodeType: string
|
|
title: string
|
|
isValueFetched?: boolean
|
|
var: VarInInspect
|
|
nodeData: NodeProps['data']
|
|
}
|
|
|
|
const Panel: FC = () => {
|
|
const { t } = useTranslation()
|
|
|
|
const bottomPanelWidth = useStore((s) => s.bottomPanelWidth)
|
|
const setShowVariableInspectPanel = useStore((s) => s.setShowVariableInspectPanel)
|
|
const [showLeftPanel, setShowLeftPanel] = useState(true)
|
|
const isListening = useStore((s) => s.isListening)
|
|
|
|
const environmentVariables = useStore((s) => s.environmentVariables)
|
|
const currentFocusNodeId = useStore((s) => s.currentFocusNodeId)
|
|
const setCurrentFocusNodeId = useStore((s) => s.setCurrentFocusNodeId)
|
|
const [currentVarId, setCurrentVarId] = useState('')
|
|
|
|
const { conversationVars, systemVars, nodesWithInspectVars, fetchInspectVarValue } =
|
|
useCurrentVars()
|
|
|
|
const isEmpty = useMemo(() => {
|
|
const allVars = [
|
|
...environmentVariables,
|
|
...conversationVars,
|
|
...systemVars,
|
|
...nodesWithInspectVars,
|
|
]
|
|
return allVars.length === 0
|
|
}, [environmentVariables, conversationVars, systemVars, nodesWithInspectVars])
|
|
|
|
const currentNodeInfo = useMemo(() => {
|
|
if (!currentFocusNodeId) return
|
|
if (currentFocusNodeId === VarInInspectType.environment) {
|
|
const currentVar = environmentVariables.find((v) => v.id === currentVarId)
|
|
const res = {
|
|
nodeId: VarInInspectType.environment,
|
|
title: VarInInspectType.environment,
|
|
nodeType: VarInInspectType.environment,
|
|
}
|
|
if (currentVar) {
|
|
return {
|
|
...res,
|
|
var: {
|
|
...currentVar,
|
|
type: VarInInspectType.environment,
|
|
visible: true,
|
|
...(currentVar.value_type === 'secret' ? { value: '******************' } : {}),
|
|
},
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
if (currentFocusNodeId === VarInInspectType.conversation) {
|
|
const currentVar = conversationVars.find((v) => v.id === currentVarId)
|
|
const res = {
|
|
nodeId: VarInInspectType.conversation,
|
|
title: VarInInspectType.conversation,
|
|
nodeType: VarInInspectType.conversation,
|
|
}
|
|
if (currentVar) {
|
|
return {
|
|
...res,
|
|
var: {
|
|
...currentVar,
|
|
type: VarInInspectType.conversation,
|
|
},
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
if (currentFocusNodeId === VarInInspectType.system) {
|
|
const currentVar = systemVars.find((v) => v.id === currentVarId)
|
|
const res = {
|
|
nodeId: VarInInspectType.system,
|
|
title: VarInInspectType.system,
|
|
nodeType: VarInInspectType.system,
|
|
}
|
|
if (currentVar) {
|
|
return {
|
|
...res,
|
|
var: {
|
|
...currentVar,
|
|
type: VarInInspectType.system,
|
|
},
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
const targetNode = nodesWithInspectVars.find((node) => node.nodeId === currentFocusNodeId)
|
|
if (!targetNode) return
|
|
const currentVar = targetNode.vars.find((v) => v.id === currentVarId)
|
|
return {
|
|
nodeId: targetNode.nodeId,
|
|
nodeType: targetNode.nodeType,
|
|
title: targetNode.title,
|
|
isSingRunRunning: targetNode.isSingRunRunning,
|
|
isValueFetched: targetNode.isValueFetched,
|
|
nodeData: targetNode.nodePayload,
|
|
...(currentVar ? { var: currentVar } : {}),
|
|
}
|
|
}, [
|
|
currentFocusNodeId,
|
|
currentVarId,
|
|
environmentVariables,
|
|
conversationVars,
|
|
systemVars,
|
|
nodesWithInspectVars,
|
|
])
|
|
|
|
const isCurrentNodeVarValueFetching = useMemo(() => {
|
|
if (!currentNodeInfo) return false
|
|
const targetNode = nodesWithInspectVars.find((node) => node.nodeId === currentNodeInfo.nodeId)
|
|
if (!targetNode) return false
|
|
return !targetNode.isValueFetched
|
|
}, [currentNodeInfo, nodesWithInspectVars])
|
|
|
|
const handleNodeVarSelect = useCallback(
|
|
(node: currentVarType) => {
|
|
setCurrentFocusNodeId(node.nodeId)
|
|
setCurrentVarId(node.var.id)
|
|
},
|
|
[setCurrentFocusNodeId, setCurrentVarId],
|
|
)
|
|
|
|
const { isLoading, schemaTypeDefinitions } = useMatchSchemaType()
|
|
const { eventEmitter } = useEventEmitterContextContext()
|
|
|
|
const handleStopListening = useCallback(() => {
|
|
eventEmitter?.emit({ type: EVENT_WORKFLOW_STOP } as any)
|
|
}, [eventEmitter])
|
|
|
|
useEffect(() => {
|
|
if (currentFocusNodeId && currentVarId && !isLoading) {
|
|
const targetNode = nodesWithInspectVars.find((node) => node.nodeId === currentFocusNodeId)
|
|
if (targetNode && !targetNode.isValueFetched)
|
|
fetchInspectVarValue([currentFocusNodeId], schemaTypeDefinitions!)
|
|
}
|
|
}, [
|
|
currentFocusNodeId,
|
|
currentVarId,
|
|
nodesWithInspectVars,
|
|
fetchInspectVarValue,
|
|
schemaTypeDefinitions,
|
|
isLoading,
|
|
])
|
|
|
|
if (isListening) {
|
|
return (
|
|
<div className={cn('flex h-full flex-col')}>
|
|
<div className="flex shrink-0 items-center justify-between pt-2 pr-2 pl-4">
|
|
<div className="system-sm-semibold-uppercase text-text-primary">
|
|
{t(($) => $['debug.variableInspect.title'], { ns: 'workflow' })}
|
|
</div>
|
|
<ActionButton onClick={() => setShowVariableInspectPanel(false)}>
|
|
<RiCloseLine className="size-4" />
|
|
</ActionButton>
|
|
</div>
|
|
<div className="grow p-2">
|
|
<Listening onStop={handleStopListening} />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (isEmpty) {
|
|
return (
|
|
<div className={cn('flex h-full flex-col')}>
|
|
<div className="flex shrink-0 items-center justify-between pt-2 pr-2 pl-4">
|
|
<div className="system-sm-semibold-uppercase text-text-primary">
|
|
{t(($) => $['debug.variableInspect.title'], { ns: 'workflow' })}
|
|
</div>
|
|
<ActionButton onClick={() => setShowVariableInspectPanel(false)}>
|
|
<RiCloseLine className="size-4" />
|
|
</ActionButton>
|
|
</div>
|
|
<div className="grow p-2">
|
|
<Empty />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className={cn('relative flex h-full')}>
|
|
{/* left */}
|
|
{bottomPanelWidth < 488 && showLeftPanel && (
|
|
<div
|
|
className="absolute top-0 left-0 size-full"
|
|
onClick={() => setShowLeftPanel(false)}
|
|
></div>
|
|
)}
|
|
<div
|
|
className={cn(
|
|
'w-60 shrink-0 border-r border-divider-burn',
|
|
bottomPanelWidth < 488
|
|
? showLeftPanel
|
|
? 'absolute top-0 left-0 z-10 h-full w-[217px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg backdrop-blur-xs'
|
|
: 'hidden'
|
|
: 'block',
|
|
)}
|
|
>
|
|
<Left
|
|
currentNodeVar={currentNodeInfo as currentVarType}
|
|
handleVarSelect={handleNodeVarSelect}
|
|
/>
|
|
</div>
|
|
{/* right */}
|
|
<div className="w-0 grow">
|
|
<Right
|
|
nodeId={currentFocusNodeId!}
|
|
isValueFetching={isCurrentNodeVarValueFetching}
|
|
currentNodeVar={currentNodeInfo as currentVarType}
|
|
handleOpenMenu={() => setShowLeftPanel(true)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default Panel
|