import type { VarInspectValue } from './value-types' import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { VarInInspect } from '@/types/workflow' import { useDebounceFn } from 'ahooks' import * as React from 'react' import { useEffect, useMemo, useRef, useState } from 'react' import { getProcessedFiles } from '@/app/components/base/file-uploader/utils' import { useStore } from '@/app/components/workflow/store' import { cn } from '@/utils/classnames' import BoolValue from '../panel/chat-variable-panel/components/bool-value' import { BoolArraySection, ErrorMessages, FileEditorSection, JsonEditorSection, TextEditorSection, } from './value-content-sections' import { formatInspectFileValue, getValueEditorState, isFileValueUploaded, validateInspectJsonValue, } from './value-content.helpers' type Props = { currentVar: VarInInspect handleValueChange: (varId: string, value: VarInspectValue) => void isTruncated: boolean } const ValueContent = ({ currentVar, handleValueChange, isTruncated, }: Props) => { const contentContainerRef = useRef(null) const errorMessageRef = useRef(null) const [editorHeight, setEditorHeight] = useState(0) const { showTextEditor, showBoolEditor, showBoolArrayEditor, isSysFiles, showJSONEditor, showFileEditor, textEditorDisabled, JSONEditorDisabled, hasChunks, } = useMemo(() => getValueEditorState(currentVar), [currentVar]) const fileUploadConfig = useStore(s => s.fileUploadConfig) const [value, setValue] = useState() const [json, setJson] = useState('') const [parseError, setParseError] = useState(null) const [validationError, setValidationError] = useState('') const [fileValue, setFileValue] = useState(() => formatInspectFileValue(currentVar)) const { run: debounceValueChange } = useDebounceFn(handleValueChange, { wait: 500 }) // update default value when id or value changed useEffect(() => { if (showTextEditor) { if (currentVar.value_type === 'number') return setValue(JSON.stringify(currentVar.value)) if (!currentVar.value) return setValue('') setValue(currentVar.value) } if (showJSONEditor) setJson(currentVar.value != null ? JSON.stringify(currentVar.value, null, 2) : '') if (showFileEditor) setFileValue(formatInspectFileValue(currentVar)) }, [currentVar.id, currentVar.value]) const handleTextChange = (value: string) => { if (isTruncated) return if (currentVar.value_type === 'string') setValue(value) if (currentVar.value_type === 'number') { if (/^-?\d+(?:\.\d+)?$/.test(value)) setValue(Number.parseFloat(value)) } const newValue = currentVar.value_type === 'number' ? Number.parseFloat(value) : value debounceValueChange(currentVar.id, newValue) } const jsonValueValidate = (value: string, type: string) => { const result = validateInspectJsonValue(value, type) setParseError(result.parseError) setValidationError(result.validationError) return result.success } const handleEditorChange = (value: string) => { if (isTruncated) return setJson(value) if (jsonValueValidate(value, currentVar.value_type)) { const parsed = JSON.parse(value) debounceValueChange(currentVar.id, parsed) } } const handleFileChange = (value: FileEntity[]) => { setFileValue(value) const processedFiles = getProcessedFiles(value) if (!isFileValueUploaded(processedFiles)) return if (currentVar.value_type === 'file') debounceValueChange(currentVar.id, processedFiles[0]) if (currentVar.value_type === 'array[file]' || isSysFiles) debounceValueChange(currentVar.id, processedFiles) } // get editor height useEffect(() => { if (contentContainerRef.current && errorMessageRef.current) { const errorMessageObserver = new ResizeObserver((entries) => { for (const entry of entries) { const borderBoxSize = Array.isArray(entry.borderBoxSize) ? entry.borderBoxSize[0] : entry.borderBoxSize const errorHeight = borderBoxSize?.blockSize ?? entry.contentRect?.height ?? 0 const containerHeight = contentContainerRef.current?.clientHeight ?? 0 setEditorHeight(Math.max(containerHeight - errorHeight, 0)) } }) errorMessageObserver.observe(errorMessageRef.current) return () => { errorMessageObserver.disconnect() } } }, [setEditorHeight]) return (
{showTextEditor && ( )} {showBoolEditor && (
{ setValue(newValue) debounceValueChange(currentVar.id, newValue) }} />
)} { showBoolArrayEditor && ( { setValue(newArray) debounceValueChange(currentVar.id, newArray) }} /> ) } {showJSONEditor && ( )} {showFileEditor && ( )}
) } export default React.memo(ValueContent)