mirror of
https://github.com/langgenius/dify.git
synced 2025-09-03 14:23:48 +00:00
198 lines
7.2 KiB
TypeScript
198 lines
7.2 KiB
TypeScript
import {
|
|
memo,
|
|
useCallback,
|
|
useMemo,
|
|
useRef,
|
|
useState,
|
|
} from 'react'
|
|
import { useStore } from '@/app/components/workflow/store'
|
|
import { RiCloseLine, RiEyeLine } from '@remixicon/react'
|
|
import type { Node } from '@/app/components/workflow/types'
|
|
import { BlockEnum } from '@/app/components/workflow/types'
|
|
import DialogWrapper from './dialog-wrapper'
|
|
import FieldList from './field-list'
|
|
import FooterTip from './footer-tip'
|
|
import GlobalInputs from './label-right-content/global-inputs'
|
|
import Datasource from './label-right-content/datasource'
|
|
import { useNodes } from 'reactflow'
|
|
import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { useNodesSyncDraft } from '@/app/components/workflow/hooks'
|
|
import type { InputVar, RAGPipelineVariables } from '@/models/pipeline'
|
|
import Button from '@/app/components/base/button'
|
|
import Divider from '@/app/components/base/divider'
|
|
import Tooltip from '@/app/components/base/tooltip'
|
|
import cn from '@/utils/classnames'
|
|
import PreviewPanel from './preview'
|
|
|
|
type InputFieldDialogProps = {
|
|
readonly?: boolean
|
|
}
|
|
|
|
const InputFieldDialog = ({
|
|
readonly = false,
|
|
}: InputFieldDialogProps) => {
|
|
const { t } = useTranslation()
|
|
const nodes = useNodes<DataSourceNodeType>()
|
|
const showInputFieldDialog = useStore(state => state.showInputFieldDialog)
|
|
const setShowInputFieldDialog = useStore(state => state.setShowInputFieldDialog)
|
|
const ragPipelineVariables = useStore(state => state.ragPipelineVariables)
|
|
const setRagPipelineVariables = useStore(state => state.setRagPipelineVariables)
|
|
const [previewPanelOpen, setPreviewPanelOpen] = useState(false)
|
|
|
|
const getInputFieldsMap = () => {
|
|
const inputFieldsMap: Record<string, InputVar[]> = {}
|
|
ragPipelineVariables?.forEach((variable) => {
|
|
const { belong_to_node_id: nodeId, ...varConfig } = variable
|
|
if (inputFieldsMap[nodeId])
|
|
inputFieldsMap[nodeId].push(varConfig)
|
|
else
|
|
inputFieldsMap[nodeId] = [varConfig]
|
|
})
|
|
return inputFieldsMap
|
|
}
|
|
const inputFieldsMap = useRef(getInputFieldsMap())
|
|
|
|
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
|
|
|
const datasourceNodeDataMap = useMemo(() => {
|
|
const datasourceNodeDataMap: Record<string, DataSourceNodeType> = {}
|
|
const datasourceNodes: Node<DataSourceNodeType>[] = nodes.filter(node => node.data.type === BlockEnum.DataSource)
|
|
datasourceNodes.forEach((node) => {
|
|
const { id, data } = node
|
|
datasourceNodeDataMap[id] = data
|
|
})
|
|
return datasourceNodeDataMap
|
|
}, [nodes])
|
|
|
|
const updateInputFields = useCallback(async (key: string, value: InputVar[]) => {
|
|
inputFieldsMap.current[key] = value
|
|
const datasourceNodeInputFields: RAGPipelineVariables = []
|
|
const globalInputFields: RAGPipelineVariables = []
|
|
Object.keys(inputFieldsMap.current).forEach((key) => {
|
|
const inputFields = inputFieldsMap.current[key]
|
|
inputFields.forEach((inputField) => {
|
|
if (key === 'shared') {
|
|
globalInputFields.push({
|
|
...inputField,
|
|
belong_to_node_id: key,
|
|
})
|
|
}
|
|
else {
|
|
datasourceNodeInputFields.push({
|
|
...inputField,
|
|
belong_to_node_id: key,
|
|
})
|
|
}
|
|
})
|
|
})
|
|
// Datasource node input fields come first, then global input fields
|
|
const newRagPipelineVariables = [...datasourceNodeInputFields, ...globalInputFields]
|
|
setRagPipelineVariables?.(newRagPipelineVariables)
|
|
handleSyncWorkflowDraft()
|
|
}, [setRagPipelineVariables, handleSyncWorkflowDraft])
|
|
|
|
const closePanel = useCallback(() => {
|
|
setShowInputFieldDialog?.(false)
|
|
}, [setShowInputFieldDialog])
|
|
|
|
const togglePreviewPanel = useCallback(() => {
|
|
setPreviewPanelOpen(prev => !prev)
|
|
}, [])
|
|
|
|
const allVariableNames = useMemo(() => {
|
|
return ragPipelineVariables?.map(variable => variable.variable) || []
|
|
}, [ragPipelineVariables])
|
|
|
|
return (
|
|
<>
|
|
<DialogWrapper
|
|
show={!!showInputFieldDialog}
|
|
onClose={closePanel}
|
|
className='grow rounded-l-2xl border-y-[0.5px] border-l-[0.5px]'
|
|
>
|
|
<div className='flex grow flex-col'>
|
|
<div className='flex items-center p-4 pb-0'>
|
|
<div className='system-xl-semibold grow'>
|
|
{t('datasetPipeline.inputFieldPanel.title')}
|
|
</div>
|
|
<Button
|
|
variant={'ghost'}
|
|
size='small'
|
|
className={cn(
|
|
'shrink-0 gap-x-px px-1.5',
|
|
previewPanelOpen && 'bg-state-accent-active text-text-accent',
|
|
)}
|
|
onClick={togglePreviewPanel}
|
|
>
|
|
<RiEyeLine className='size-3.5' />
|
|
<span className='px-[3px]'>{t('datasetPipeline.operations.preview')}</span>
|
|
</Button>
|
|
<Divider type='vertical' className='mx-1 h-3' />
|
|
<button
|
|
type='button'
|
|
className='flex size-6 shrink-0 items-center justify-center p-0.5'
|
|
onClick={closePanel}
|
|
>
|
|
<RiCloseLine className='size-4 text-text-tertiary' />
|
|
</button>
|
|
</div>
|
|
<div className='system-sm-regular px-4 pb-2 pt-1 text-text-tertiary'>
|
|
{t('datasetPipeline.inputFieldPanel.description')}
|
|
</div>
|
|
<div className='flex grow flex-col overflow-y-auto'>
|
|
{/* Unique Inputs for Each Entrance */}
|
|
<div className='flex h-6 items-center gap-x-0.5 px-4 pt-2'>
|
|
<span className='system-sm-semibold-uppercase text-text-secondary'>
|
|
{t('datasetPipeline.inputFieldPanel.uniqueInputs.title')}
|
|
</span>
|
|
<Tooltip
|
|
popupContent={t('datasetPipeline.inputFieldPanel.uniqueInputs.tooltip')}
|
|
popupClassName='max-w-[240px]'
|
|
/>
|
|
</div>
|
|
<div className='flex flex-col gap-y-1 py-1'>
|
|
{
|
|
Object.keys(datasourceNodeDataMap).map((key) => {
|
|
const inputFields = inputFieldsMap.current[key] || []
|
|
return (
|
|
<FieldList
|
|
key={key}
|
|
nodeId={key}
|
|
LabelRightContent={<Datasource nodeData={datasourceNodeDataMap[key]} />}
|
|
inputFields={inputFields}
|
|
readonly={readonly}
|
|
labelClassName='pt-1 pb-1'
|
|
handleInputFieldsChange={updateInputFields}
|
|
allVariableNames={allVariableNames}
|
|
/>
|
|
)
|
|
})
|
|
}
|
|
</div>
|
|
{/* Global Inputs */}
|
|
<FieldList
|
|
nodeId='shared'
|
|
LabelRightContent={<GlobalInputs />}
|
|
inputFields={inputFieldsMap.current.shared || []}
|
|
readonly={readonly}
|
|
labelClassName='pt-2 pb-1'
|
|
handleInputFieldsChange={updateInputFields}
|
|
allVariableNames={allVariableNames}
|
|
/>
|
|
</div>
|
|
<FooterTip />
|
|
</div>
|
|
</DialogWrapper>
|
|
{previewPanelOpen && (
|
|
<PreviewPanel
|
|
show={previewPanelOpen}
|
|
onClose={togglePreviewPanel}
|
|
/>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default memo(InputFieldDialog)
|