feat: Implement input field management panel

This commit is contained in:
twwu 2025-08-01 16:27:53 +08:00
parent ec501cf664
commit 44d569a7c1
35 changed files with 264 additions and 364 deletions

View File

@ -1,7 +1,7 @@
import type { ZodSchema, ZodString } from 'zod'
import { z } from 'zod'
import { type InputFieldConfiguration, InputFieldType } from './types'
import { SupportedFileTypes, TransferMethod } from '@/app/components/rag-pipeline/components/input-field/editor/form/schema'
import { SupportedFileTypes, TransferMethod } from '@/app/components/rag-pipeline/components/panel/input-field/editor/form/schema'
export const generateZodSchema = (fields: InputFieldConfiguration[]) => {
const shape: Record<string, ZodSchema> = {}

View File

@ -1,66 +0,0 @@
import { useCallback } from 'react'
import type { ReactNode } from 'react'
import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react'
import cn from '@/utils/classnames'
type DialogWrapperProps = {
dialogClassName?: string
className?: string
panelWrapperClassName?: string
outerWrapperClassName?: string
children: ReactNode
show: boolean
onClose?: () => void
}
const DialogWrapper = ({
dialogClassName,
className,
panelWrapperClassName,
outerWrapperClassName,
children,
show,
onClose,
}: DialogWrapperProps) => {
const close = useCallback(() => onClose?.(), [onClose])
return (
<Transition appear show={show}>
<Dialog
as='div'
className={cn('relative z-40', dialogClassName)}
onClose={close}
>
<TransitionChild>
<div
className={cn(
'fixed inset-0 bg-black/25',
'data-[closed]:opacity-0',
'data-[enter]:opacity-100 data-[enter]:duration-300 data-[enter]:ease-out',
'data-[leave]:opacity-0 data-[leave]:duration-200 data-[leave]:ease-in',
)}
/>
</TransitionChild>
<div className={cn('fixed inset-0', outerWrapperClassName)}>
<div className={cn('flex min-h-full flex-col items-end justify-center pb-1 pt-[116px]', panelWrapperClassName)}>
<TransitionChild>
<DialogPanel
className={cn(
'relative flex w-[420px] flex-col overflow-hidden border-components-panel-border bg-components-panel-bg-alt p-0 shadow-xl shadow-shadow-shadow-5 transition-all',
'data-[closed]:scale-95 data-[closed]:opacity-0',
'data-[enter]:scale-100 data-[enter]:opacity-100 data-[enter]:duration-300 data-[enter]:ease-out',
'data-[leave]:scale-95 data-[leave]:opacity-0 data-[leave]:duration-200 data-[leave]:ease-in',
className,
)}
>
{children}
</DialogPanel>
</TransitionChild>
</div>
</div>
</Dialog>
</Transition >
)
}
export default DialogWrapper

View File

@ -1,197 +0,0 @@
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}
outerWrapperClassName='overflow-y-auto'
panelWrapperClassName='justify-start'
className='grow rounded-l-2xl border-y-[0.5px] border-l-[0.5px]'
>
<div className='flex shrink-0 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 shrink-0 px-4 pb-2 pt-1 text-text-tertiary'>
{t('datasetPipeline.inputFieldPanel.description')}
</div>
<div className='flex grow flex-col overflow-hidden'>
{/* 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 />
</DialogWrapper>
{previewPanelOpen && (
<PreviewPanel
show={previewPanelOpen}
onClose={togglePreviewPanel}
/>
)}
</>
)
}
export default memo(InputFieldDialog)

View File

@ -7,22 +7,38 @@ import Panel from '@/app/components/workflow/panel'
import { useStore } from '@/app/components/workflow/store'
import Record from '@/app/components/workflow/panel/record'
import TestRunPanel from './test-run'
import InputFieldPanel from './input-field'
import PreviewPanel from './input-field/preview'
import InputFieldEditorPanel from './input-field/editor'
const RagPipelinePanelOnRight = () => {
const historyWorkflowData = useStore(s => s.historyWorkflowData)
const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel)
return (
<>
{
historyWorkflowData && (
<Record />
)
}
{historyWorkflowData && <Record />}
{showDebugAndPreviewPanel && <TestRunPanel />}
</>
)
}
const RagPipelinePanelOnLeft = () => {
const showInputFieldPanel = useStore(s => s.showInputFieldPanel)
const showInputFieldPreviewPanel = useStore(s => s.showInputFieldPreviewPanel)
const inputFieldEditPanelProps = useStore(s => s.inputFieldEditPanelProps)
return (
<>
{showInputFieldPreviewPanel && <PreviewPanel />}
{inputFieldEditPanelProps && (
<InputFieldEditorPanel
{...inputFieldEditPanelProps}
/>
)}
{showInputFieldPanel && <InputFieldPanel />}
</>
)
}
const RagPipelinePanel = () => {
const pipelineId = useStore(s => s.pipelineId)
const versionHistoryPanelProps = useMemo(() => {
@ -37,7 +53,7 @@ const RagPipelinePanel = () => {
const panelProps: PanelProps = useMemo(() => {
return {
components: {
left: null,
left: <RagPipelinePanelOnLeft />,
right: <RagPipelinePanelOnRight />,
},
versionHistoryPanelProps,

View File

@ -1,5 +1,4 @@
import { RiCloseLine } from '@remixicon/react'
import DialogWrapper from '../dialog-wrapper'
import InputFieldForm from './form'
import { convertFormDataToINputField, convertToInputFieldFormData } from './utils'
import { useCallback } from 'react'
@ -8,15 +7,13 @@ import type { InputVar } from '@/models/pipeline'
import type { FormData } from './form/types'
import type { MoreInfo } from '@/app/components/workflow/types'
type InputFieldEditorProps = {
show: boolean
export type InputFieldEditorProps = {
onClose: () => void
onSubmit: (data: InputVar, moreInfo?: MoreInfo) => void
initialData?: InputVar
}
const InputFieldEditor = ({
show,
const InputFieldEditorPanel = ({
onClose,
onSubmit,
initialData,
@ -30,13 +27,7 @@ const InputFieldEditor = ({
}, [onSubmit])
return (
<DialogWrapper
show={show}
onClose={onClose}
outerWrapperClassName='overflow-y-auto'
panelWrapperClassName='pr-[424px] justify-start'
className='w-[400px] rounded-2xl border-[0.5px] bg-components-panel-bg shadow-shadow-shadow-9'
>
<div className='relative mr-1 flex h-fit max-h-full w-[400px] flex-col overflow-y-auto rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9'>
<div className='system-xl-semibold flex items-center pb-1 pl-4 pr-11 pt-3.5 text-text-primary'>
{initialData ? t('datasetPipeline.inputFieldPanel.editInputField') : t('datasetPipeline.inputFieldPanel.addInputField')}
</div>
@ -53,8 +44,8 @@ const InputFieldEditor = ({
onCancel={onClose}
onSubmit={handleSubmit}
/>
</DialogWrapper>
</div>
)
}
export default InputFieldEditor
export default InputFieldEditorPanel

View File

@ -10,8 +10,9 @@ import type { MoreInfo, ValueSelector } from '@/app/components/workflow/types'
import { ChangeType } from '@/app/components/workflow/types'
import { useBoolean } from 'ahooks'
import Toast from '@/app/components/base/toast'
import { usePipeline } from '../../../hooks/use-pipeline'
import { usePipeline } from '../../../../hooks/use-pipeline'
import { useTranslation } from 'react-i18next'
import { useStore } from '@/app/components/workflow/store'
const VARIABLE_PREFIX = 'rag'
@ -29,6 +30,7 @@ export const useFieldList = ({
allVariableNames,
}: useFieldListProps) => {
const { t } = useTranslation()
const setInputFieldEditPanelProps = useStore(s => s.setInputFieldEditPanelProps)
const [inputFields, setInputFields] = useState<InputVar[]>(initialInputFields)
const inputFieldsRef = useRef<InputVar[]>(inputFields)
const [removedVar, setRemovedVar] = useState<ValueSelector>([])
@ -55,20 +57,12 @@ export const useFieldList = ({
handleInputFieldsChange(newInputFields)
}, [handleInputFieldsChange])
const [editingField, setEditingField] = useState<InputVar | undefined>()
const [showInputFieldEditor, setShowInputFieldEditor] = useState(false)
const editingFieldIndex = useRef<number>(-1)
const handleOpenInputFieldEditor = useCallback((id?: string) => {
const index = inputFieldsRef.current.findIndex(field => field.variable === id)
editingFieldIndex.current = index
setEditingField(inputFieldsRef.current[index])
setShowInputFieldEditor(true)
}, [])
const handleCloseInputFieldEditor = useCallback(() => {
setShowInputFieldEditor(false)
setInputFieldEditPanelProps?.(null)
editingFieldIndex.current = -1
setEditingField(undefined)
}, [])
}, [setInputFieldEditPanelProps])
const handleRemoveField = useCallback((index: number) => {
const itemToRemove = inputFieldsRef.current[index]
@ -92,7 +86,7 @@ export const useFieldList = ({
const handleSubmitField = useCallback((data: InputVar, moreInfo?: MoreInfo) => {
const isDuplicate = allVariableNames.some(name =>
name === data.variable && name !== editingField?.variable)
name === data.variable && name !== inputFieldsRef.current[editingFieldIndex.current]?.variable)
if (isDuplicate) {
Toast.notify({
type: 'error',
@ -113,18 +107,23 @@ export const useFieldList = ({
if (moreInfo?.type === ChangeType.changeVarName)
handleInputVarRename(nodeId, [VARIABLE_PREFIX, nodeId, moreInfo.payload?.beforeKey || ''], [VARIABLE_PREFIX, nodeId, moreInfo.payload?.afterKey || ''])
handleCloseInputFieldEditor()
}, [allVariableNames, editingField?.variable, handleCloseInputFieldEditor, handleInputFieldsChange, handleInputVarRename, nodeId, t])
}, [allVariableNames, handleCloseInputFieldEditor, handleInputFieldsChange, handleInputVarRename, nodeId, t])
const handleOpenInputFieldEditor = useCallback((id?: string) => {
const index = inputFieldsRef.current.findIndex(field => field.variable === id)
editingFieldIndex.current = index
setInputFieldEditPanelProps?.({
onClose: handleCloseInputFieldEditor,
onSubmit: handleSubmitField,
initialData: inputFieldsRef.current[index],
})
}, [])
return {
inputFields,
handleInputFieldsChange,
handleListSortChange,
handleRemoveField,
handleSubmitField,
editingField,
showInputFieldEditor,
handleOpenInputFieldEditor,
handleCloseInputFieldEditor,
isShowRemoveVarConfirm,
hideRemoveVarConfirm,
onRemoveVarConfirm,

View File

@ -1,7 +1,6 @@
import React, { useCallback } from 'react'
import { RiAddLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import InputFieldEditor from '../editor'
import type { InputVar } from '@/models/pipeline'
import ActionButton from '@/app/components/base/action-button'
import { useFieldList } from './hooks'
@ -33,13 +32,9 @@ const FieldList = ({
const {
inputFields,
handleSubmitField,
handleListSortChange,
handleRemoveField,
handleCloseInputFieldEditor,
handleOpenInputFieldEditor,
showInputFieldEditor,
editingField,
isShowRemoveVarConfirm,
hideRemoveVarConfirm,
onRemoveVarConfirm,
@ -59,6 +54,7 @@ const FieldList = ({
<ActionButton
onClick={() => handleOpenInputFieldEditor()}
disabled={readonly}
className={cn(readonly && 'cursor-not-allowed')}
>
<RiAddLine className='h-4 w-4 text-text-tertiary' />
</ActionButton>
@ -71,14 +67,6 @@ const FieldList = ({
onListSortChange={handleListSortChange}
readonly={readonly}
/>
{showInputFieldEditor && (
<InputFieldEditor
show={showInputFieldEditor}
initialData={editingField}
onSubmit={handleSubmitField}
onClose={handleCloseInputFieldEditor}
/>
)}
<RemoveEffectVarConfirm
isShow={isShowRemoveVarConfirm}
onCancel={hideRemoveVarConfirm}

View File

@ -0,0 +1,175 @@
import {
memo,
useCallback,
useMemo,
useRef,
} 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 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'
const InputFieldPanel = () => {
const { t } = useTranslation()
const nodes = useNodes<DataSourceNodeType>()
const setShowInputFieldPanel = useStore(state => state.setShowInputFieldPanel)
const ragPipelineVariables = useStore(state => state.ragPipelineVariables)
const setRagPipelineVariables = useStore(state => state.setRagPipelineVariables)
const showInputFieldPreviewPanel = useStore(state => state.showInputFieldPreviewPanel)
const setShowInputFieldPreviewPanel = useStore(state => state.setShowInputFieldPreviewPanel)
const inputFieldEditPanelProps = useStore(state => state.inputFieldEditPanelProps)
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(() => {
setShowInputFieldPanel?.(false)
}, [setShowInputFieldPanel])
const togglePreviewPanel = useCallback(() => {
setShowInputFieldPreviewPanel?.(true)
}, [setShowInputFieldPreviewPanel])
const allVariableNames = useMemo(() => {
return ragPipelineVariables?.map(variable => variable.variable) || []
}, [ragPipelineVariables])
return (
<div className='mr-1 flex h-full w-[400px] flex-col rounded-2xl border-y-[0.5px] border-l-[0.5px] border-components-panel-border bg-components-panel-bg-alt shadow-xl shadow-shadow-shadow-5'>
<div className='flex shrink-0 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',
showInputFieldPreviewPanel && '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 shrink-0 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={showInputFieldPreviewPanel || !!inputFieldEditPanelProps}
labelClassName='pt-1 pb-1'
handleInputFieldsChange={updateInputFields}
allVariableNames={allVariableNames}
/>
)
})
}
</div>
{/* Global Inputs */}
<FieldList
nodeId='shared'
LabelRightContent={<GlobalInputs />}
inputFields={inputFieldsMap.current.shared || []}
readonly={showInputFieldPreviewPanel || !!inputFieldEditPanelProps}
labelClassName='pt-2 pb-1'
handleInputFieldsChange={updateInputFields}
allVariableNames={allVariableNames}
/>
</div>
<FooterTip />
</div>
)
}
export default memo(InputFieldPanel)

View File

@ -1,8 +1,8 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import DataSourceOptions from '../../panel/test-run/data-source-options'
import DataSourceOptions from '../../test-run/data-source-options'
import Form from './form'
import type { Datasource } from '../../panel/test-run/types'
import type { Datasource } from '../../test-run/types'
import { useStore } from '@/app/components/workflow/store'
import { useDraftPipelinePreProcessingParams } from '@/service/use-pipeline'

View File

@ -1,33 +1,24 @@
import { useState } from 'react'
import { useCallback, useState } from 'react'
import { RiCloseLine } from '@remixicon/react'
import DialogWrapper from '../dialog-wrapper'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import DataSource from './data-source'
import Divider from '@/app/components/base/divider'
import ProcessDocuments from './process-documents'
import type { Datasource } from '../../panel/test-run/types'
import type { Datasource } from '../../test-run/types'
import { useStore } from '@/app/components/workflow/store'
type PreviewPanelProps = {
show: boolean
onClose: () => void
}
const PreviewPanel = ({
show,
onClose,
}: PreviewPanelProps) => {
const PreviewPanel = () => {
const { t } = useTranslation()
const [datasource, setDatasource] = useState<Datasource>()
const setShowInputFieldPreviewPanel = useStore(state => state.setShowInputFieldPreviewPanel)
const handleClosePreviewPanel = useCallback(() => {
setShowInputFieldPreviewPanel(false)
}, [setShowInputFieldPreviewPanel])
return (
<DialogWrapper
show={show}
onClose={onClose}
outerWrapperClassName='overflow-y-auto'
panelWrapperClassName='pr-[424px] justify-start'
className='w-[480px] grow rounded-2xl border-[0.5px] bg-components-panel-bg'
>
<div className='mr-1 flex h-full w-[480px] flex-col overflow-y-auto rounded-2xl border-y-[0.5px] border-l-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5'>
<div className='flex items-center gap-x-2 px-4 pt-1'>
<div className='grow py-1'>
<Badge className='border-text-accent-secondary bg-components-badge-bg-dimm text-text-accent-secondary'>
@ -37,7 +28,7 @@ const PreviewPanel = ({
<button
type='button'
className='flex size-6 shrink-0 items-center justify-center'
onClick={onClose}
onClick={handleClosePreviewPanel}
>
<RiCloseLine className='size-4 text-text-tertiary' />
</button>
@ -52,7 +43,7 @@ const PreviewPanel = ({
</div>
{/* Process documents form Preview */}
<ProcessDocuments dataSourceNodeId={datasource?.nodeId || ''} />
</DialogWrapper>
</div>
)
}

View File

@ -3,7 +3,6 @@ import {
useState,
} from 'react'
import { useStore } from '../../workflow/store'
import InputField from './input-field'
import PluginDependency from '../../workflow/plugin-dependency'
import RagPipelinePanel from './panel'
import RagPipelineHeader from './rag-pipeline-header'
@ -21,7 +20,6 @@ import PublishToast from './publish-toast'
const RagPipelineChildren = () => {
const { eventEmitter } = useEventEmitterContextContext()
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
const showInputFieldDialog = useStore(state => state.showInputFieldDialog)
const showImportDSLModal = useStore(s => s.showImportDSLModal)
const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal)
const {
@ -60,9 +58,6 @@ const RagPipelineChildren = () => {
}
<RagPipelineHeader />
<RagPipelinePanel />
{
showInputFieldDialog && (<InputField />)
}
<PublishToast />
</>
)

View File

@ -6,10 +6,10 @@ import { useTranslation } from 'react-i18next'
const InputFieldButton = () => {
const { t } = useTranslation()
const setShowInputFieldDialog = useStore(state => state.setShowInputFieldDialog)
const setShowInputFieldPanel = useStore(state => state.setShowInputFieldPanel)
const handleClick = useCallback(() => {
setShowInputFieldDialog?.(true)
}, [setShowInputFieldDialog])
setShowInputFieldPanel?.(true)
}, [setShowInputFieldPanel])
return (
<Button

View File

@ -6,13 +6,18 @@ import type {
import type { DataSourceItem } from '@/app/components/workflow/block-selector/types'
import { transformDataSourceToTool } from '@/app/components/workflow/block-selector/utils'
import type { IconInfo } from '@/models/datasets'
import type { InputFieldEditorProps } from '../components/panel/input-field/editor'
export type RagPipelineSliceShape = {
pipelineId: string
knowledgeName: string
knowledgeIcon?: IconInfo
showInputFieldDialog: boolean
setShowInputFieldDialog: (showInputFieldPanel: boolean) => void
showInputFieldPanel: boolean
setShowInputFieldPanel: (showInputFieldPanel: boolean) => void
showInputFieldPreviewPanel: boolean
setShowInputFieldPreviewPanel: (showInputFieldPreviewPanel: boolean) => void
inputFieldEditPanelProps: InputFieldEditorProps | null
setInputFieldEditPanelProps: (showInputFieldEditPanel: InputFieldEditorProps | null) => void
nodesDefaultConfigs: Record<string, any>
setNodesDefaultConfigs: (nodesDefaultConfigs: Record<string, any>) => void
ragPipelineVariables: RAGPipelineVariables
@ -25,8 +30,12 @@ export type CreateRagPipelineSliceSlice = StateCreator<RagPipelineSliceShape>
export const createRagPipelineSliceSlice: StateCreator<RagPipelineSliceShape> = set => ({
pipelineId: '',
knowledgeName: '',
showInputFieldDialog: false,
setShowInputFieldDialog: showInputFieldDialog => set(() => ({ showInputFieldDialog })),
showInputFieldPanel: false,
setShowInputFieldPanel: showInputFieldPanel => set(() => ({ showInputFieldPanel })),
showInputFieldPreviewPanel: false,
setShowInputFieldPreviewPanel: showInputFieldPreviewPanel => set(() => ({ showInputFieldPreviewPanel })),
inputFieldEditPanelProps: null,
setInputFieldEditPanelProps: inputFieldEditPanelProps => set(() => ({ inputFieldEditPanelProps })),
nodesDefaultConfigs: {},
setNodesDefaultConfigs: nodesDefaultConfigs => set(() => ({ nodesDefaultConfigs })),
ragPipelineVariables: [],

View File

@ -54,11 +54,10 @@ const Editor: FC<Props> = ({
useEffect(() => {
onFocusChange?.(isFocus)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isFocus])
const pipelineId = useStore(s => s.pipelineId)
const setShowInputFieldDialog = useStore(s => s.setShowInputFieldDialog)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
return (
<div className={cn(className, 'relative')}>
@ -108,7 +107,7 @@ const Editor: FC<Props> = ({
return acc
}, {} as any),
showManageInputField: !!pipelineId,
onManageInputField: () => setShowInputFieldDialog?.(true),
onManageInputField: () => setShowInputFieldPanel?.(true),
}}
onChange={onChange}
editable={!readOnly}

View File

@ -147,7 +147,7 @@ const Editor: FC<Props> = ({
const getVarType = useWorkflowVariableType()
const pipelineId = useStore(s => s.pipelineId)
const setShowInputFieldDialog = useStore(s => s.setShowInputFieldDialog)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
return (
<Wrap className={cn(className, wrapClassName)} style={wrapStyle} isInNode isExpand={isExpand}>
@ -273,7 +273,7 @@ const Editor: FC<Props> = ({
return acc
}, {} as any),
showManageInputField: !!pipelineId,
onManageInputField: () => setShowInputFieldDialog?.(true),
onManageInputField: () => setShowInputFieldPanel?.(true),
}}
onChange={onChange}
onBlur={setBlur}

View File

@ -27,7 +27,7 @@ const VarReferencePopup: FC<Props> = ({
const { t } = useTranslation()
const pipelineId = useStore(s => s.pipelineId)
const showManageRagInputFields = useMemo(() => !!pipelineId, [pipelineId])
const setShowInputFieldDialog = useStore(s => s.setShowInputFieldDialog)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
const docLink = useDocLink()
// max-h-[300px] overflow-y-auto todo: use portal to handle long list
return (
@ -68,7 +68,7 @@ const VarReferencePopup: FC<Props> = ({
isSupportFileVar={isSupportFileVar}
zIndex={zIndex}
showManageInputField={showManageRagInputFields}
onManageInputField={() => setShowInputFieldDialog?.(true)}
onManageInputField={() => setShowInputFieldPanel?.(true)}
/>
}
</div >

View File

@ -24,7 +24,7 @@ const ConditionInput = ({
const { t } = useTranslation()
const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey)
const pipelineId = useStore(s => s.pipelineId)
const setShowInputFieldDialog = useStore(s => s.setShowInputFieldDialog)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
return (
<PromptEditor
@ -52,7 +52,7 @@ const ConditionInput = ({
return acc
}, {} as any),
showManageInputField: !!pipelineId,
onManageInputField: () => setShowInputFieldDialog?.(true),
onManageInputField: () => setShowInputFieldPanel?.(true),
}}
onChange={onChange}
editable={!disabled}

View File

@ -21,7 +21,7 @@ const ConditionInput = ({
const { t } = useTranslation()
const controlPromptEditorRerenderKey = useStore(s => s.controlPromptEditorRerenderKey)
const pipelineId = useStore(s => s.pipelineId)
const setShowInputFieldDialog = useStore(s => s.setShowInputFieldDialog)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
return (
<PromptEditor
@ -46,7 +46,7 @@ const ConditionInput = ({
return acc
}, {} as any),
showManageInputField: !!pipelineId,
onManageInputField: () => setShowInputFieldDialog?.(true),
onManageInputField: () => setShowInputFieldPanel?.(true),
}}
onChange={onChange}
editable={!disabled}

View File

@ -39,7 +39,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
const [collapsed, setCollapsed] = React.useState(false)
const pipelineId = useStore(s => s.pipelineId)
const setShowInputFieldDialog = useStore(s => s.setShowInputFieldDialog)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
if (isLoading) {
return <div className='flex h-[200px] items-center justify-center'>
@ -65,7 +65,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
currentProvider={currCollection}
currentTool={currTool}
showManageInputField={!!pipelineId}
onManageInputField={() => setShowInputFieldDialog?.(true)}
onManageInputField={() => setShowInputFieldPanel?.(true)}
/>
</Field>
)}