diff --git a/web/app/components/workflow/variable-inspect/right.tsx b/web/app/components/workflow/variable-inspect/right.tsx index 7cc6baf9a4..b28340db12 100644 --- a/web/app/components/workflow/variable-inspect/right.tsx +++ b/web/app/components/workflow/variable-inspect/right.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { SplitRightProps } from './split-panel' -import type { VarInspectValue } from './types' +import type { VarInspectValue } from './value-types' import type { currentVarType } from './variables-tab' import type { GenRes } from '@/service/debug' import { diff --git a/web/app/components/workflow/variable-inspect/types.ts b/web/app/components/workflow/variable-inspect/types.ts index c2711294ff..f9eb952c71 100644 --- a/web/app/components/workflow/variable-inspect/types.ts +++ b/web/app/components/workflow/variable-inspect/types.ts @@ -1,7 +1,3 @@ -import type { FileEntity } from '@/app/components/base/file-uploader/types' -import type { PromptTemplateItem } from '@/app/components/workflow/types' -import type { FileResponse } from '@/types/workflow' - export const EVENT_WORKFLOW_STOP = 'WORKFLOW_STOP' export const CHUNK_SCHEMA_TYPES = ['general_structure', 'parent_child_structure', 'qa_structure'] @@ -20,16 +16,3 @@ export enum InspectTab { Variables = 'variables', Artifacts = 'artifacts', } - -export type VarInspectValue - = string - | number - | boolean - | null - | Record - | Array> - | FileEntity - | FileEntity[] - | FileResponse - | FileResponse[] - | PromptTemplateItem[] diff --git a/web/app/components/workflow/variable-inspect/value-content.tsx b/web/app/components/workflow/variable-inspect/value-content.tsx index 282ff785df..8aac0df232 100644 --- a/web/app/components/workflow/variable-inspect/value-content.tsx +++ b/web/app/components/workflow/variable-inspect/value-content.tsx @@ -1,4 +1,5 @@ -import type { VarInspectValue } from './types' +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' @@ -46,6 +47,20 @@ const jsonValueTypes = new Set([ ]) const fileValueTypes = new Set([VarType.file, VarType.arrayFile]) +type EditorState = { + textValue: string | number + jsonValue: string + fileValue: FileEntity[] +} + +const formatFileValue = (value: VarInInspect, isSysFiles: boolean): FileEntity[] => { + if (value.value_type === VarType.file) + return value.value ? getProcessedFilesFromResponse([value.value]) : [] + if (value.value_type === VarType.arrayFile || (value.type === VarInInspectType.system && isSysFiles)) + return value.value && value.value.length > 0 ? getProcessedFilesFromResponse(value.value) : [] + return [] +} + const ValueContent = ({ currentVar, handleValueChange, @@ -70,47 +85,48 @@ const ValueContent = ({ return CHUNK_SCHEMA_TYPES.includes(currentVar.schemaType) }, [currentVar.schemaType]) - const formatFileValue = (value: VarInInspect) => { - if (value.value_type === VarType.file) - return value.value ? getProcessedFilesFromResponse([value.value]) : [] - if (value.value_type === VarType.arrayFile || (value.type === VarInInspectType.system && currentVar.name === 'files')) - return value.value && value.value.length > 0 ? getProcessedFilesFromResponse(value.value) : [] - return [] - } + const initialEditorState = useMemo(() => { + const textValue = showTextEditor + ? ( + currentVar.value_type === VarType.number + ? JSON.stringify(currentVar.value) + : (currentVar.value || '') + ) + : '' + const jsonValue = showJSONEditor + ? (currentVar.value != null ? JSON.stringify(currentVar.value, null, 2) : '') + : '' + const fileValue = showFileEditor + ? formatFileValue(currentVar, isSysFiles) + : [] + return { + textValue, + jsonValue, + fileValue, + } + }, [currentVar, isSysFiles, showFileEditor, showJSONEditor, showTextEditor]) - const [value, setValue] = useState() - const [json, setJson] = useState('') + const [editorState, setEditorState] = useState(initialEditorState) const [parseError, setParseError] = useState(null) const [validationError, setValidationError] = useState('') - const [fileValue, setFileValue] = useState(() => formatFileValue(currentVar)) + const { textValue, jsonValue, fileValue } = editorState const { run: debounceValueChange } = useDebounceFn(handleValueChange, { wait: 500 }) - // update default value when id changed + // update default value when id or value changed useEffect(() => { - if (showTextEditor) { - if (currentVar.value_type === VarType.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(formatFileValue(currentVar)) - }, [currentVar.id, currentVar.value]) + setEditorState(initialEditorState) + }, [initialEditorState]) const handleTextChange = (value: string) => { if (isTruncated) return if (currentVar.value_type === VarType.string) - setValue(value) + setEditorState(prev => ({ ...prev, textValue: value })) if (currentVar.value_type === VarType.number) { if (/^-?\d+(\.)?(\d+)?$/.test(value)) - setValue(Number.parseFloat(value)) + setEditorState(prev => ({ ...prev, textValue: Number.parseFloat(value) })) } const newValue = currentVar.value_type === VarType.number ? Number.parseFloat(value) : value debounceValueChange(currentVar.id, newValue) @@ -156,25 +172,27 @@ const ValueContent = ({ const handleEditorChange = (value: string) => { if (isTruncated) return - setJson(value) + setEditorState(prev => ({ ...prev, jsonValue: value })) if (jsonValueValidate(value, currentVar.value_type)) { const parsed = JSON.parse(value) debounceValueChange(currentVar.id, parsed) } } - const fileValueValidate = (fileList: any[]) => fileList.every(file => file.upload_file_id) + type ProcessedFile = ReturnType[number] + const fileValueValidate = (fileList: ProcessedFile[]) => fileList.every(file => file.upload_file_id) - const handleFileChange = (value: any[]) => { - setFileValue(value) + const handleFileChange = (value: FileEntity[]) => { + setEditorState(prev => ({ ...prev, fileValue: value })) + const processedFiles = getProcessedFiles(value) // check every file upload progress // invoke update api after every file uploaded - if (!fileValueValidate(value)) + if (!fileValueValidate(processedFiles)) return if (currentVar.value_type === VarType.file) - debounceValueChange(currentVar.id, value[0]) + debounceValueChange(currentVar.id, processedFiles[0]) if (currentVar.value_type === VarType.arrayFile || isSysFiles) - debounceValueChange(currentVar.id, value) + debounceValueChange(currentVar.id, processedFiles) } // get editor height @@ -182,9 +200,12 @@ const ValueContent = ({ if (contentContainerRef.current && errorMessageRef.current) { const errorMessageObserver = new ResizeObserver((entries) => { for (const entry of entries) { - const { inlineSize } = entry.borderBoxSize[0] - const height = (contentContainerRef.current as any).clientHeight - inlineSize - setEditorHeight(height) + const borderBoxSize = Array.isArray(entry.borderBoxSize) + ? entry.borderBoxSize[0] + : entry.borderBoxSize + const errorHeight = borderBoxSize?.blockSize ?? entry.contentRect.height + const containerHeight = contentContainerRef.current?.clientHeight ?? 0 + setEditorHeight(Math.max(containerHeight - errorHeight, 0)) } }) errorMessageObserver.observe(errorMessageRef.current) @@ -209,7 +230,7 @@ const ValueContent = ({ handleTextChange(e.target.value)} /> ) @@ -232,7 +253,6 @@ const ValueContent = ({ { - setValue(newValue) debounceValueChange(currentVar.id, newValue) }} /> @@ -248,7 +268,6 @@ const ValueContent = ({ onChange={(newValue) => { const newArray = [...(currentVar.value as boolean[])] newArray[i] = newValue - setValue(newArray) debounceValueChange(currentVar.id, newArray) }} /> @@ -263,7 +282,7 @@ const ValueContent = ({ previewType={PreviewType.Chunks} varType={currentVar.value_type} schemaType={currentVar.schemaType ?? ''} - jsonString={json ?? '{}'} + jsonString={jsonValue ?? '{}'} readonly={JSONEditorDisabled} handleEditorChange={handleEditorChange} /> @@ -273,7 +292,7 @@ const ValueContent = ({ readonly={JSONEditorDisabled || isTruncated} className="overflow-y-auto" hideTopMenu - schema={json} + schema={jsonValue} onUpdate={handleEditorChange} isTruncated={isTruncated} /> @@ -283,7 +302,7 @@ const ValueContent = ({
handleFileChange(getProcessedFiles(files))} + onChange={handleFileChange} fileConfig={{ allowed_file_types: [ SupportUploadFileTypes.image, diff --git a/web/app/components/workflow/variable-inspect/value-types.ts b/web/app/components/workflow/variable-inspect/value-types.ts new file mode 100644 index 0000000000..b8a863e795 --- /dev/null +++ b/web/app/components/workflow/variable-inspect/value-types.ts @@ -0,0 +1,16 @@ +import type { FileEntity } from '@/app/components/base/file-uploader/types' +import type { PromptTemplateItem } from '@/app/components/workflow/types' +import type { FileResponse } from '@/types/workflow' + +export type VarInspectValue + = string + | number + | boolean + | null + | Record + | Array> + | FileEntity + | FileEntity[] + | FileResponse + | FileResponse[] + | PromptTemplateItem[]