diff --git a/web/package.json b/web/package.json index 0284f397..b41ab9b5 100644 --- a/web/package.json +++ b/web/package.json @@ -16,6 +16,7 @@ "@codemirror/lang-cpp": "^6.0.3", "@codemirror/lang-java": "^6.0.2", "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-python": "^6.2.1", "@codemirror/lang-rust": "^6.0.2", "@codemirror/state": "^6.5.4", diff --git a/web/src/components/Chat/ChatInput.tsx b/web/src/components/Chat/ChatInput.tsx index 6495ff06..046d133d 100644 --- a/web/src/components/Chat/ChatInput.tsx +++ b/web/src/components/Chat/ChatInput.tsx @@ -5,10 +5,11 @@ * @Last Modified time: 2026-03-23 17:46:25 */ import { type FC, useEffect, useMemo, useState } from 'react' -import { Flex, Input, Spin } from 'antd' +import { Flex, Input } from 'antd' import clsx from 'clsx' import type { ChatInputProps } from './types' +import FileList from './FileList' /** * Chat Input Component @@ -52,6 +53,7 @@ const ChatInput: FC = ({ })) || [] }, [fileList]) + const handleSend = () => { if (loading || !inputValue || inputValue.trim() === '') return onSend(inputValue) @@ -64,100 +66,9 @@ const ChatInput: FC = ({ - {previewFileList.length > 0 &&
- - {previewFileList.map((file) => { - if (file.type?.includes('image')) { - return ( - -
- {file.name} -
handleDelete(file)} - >
-
-
- ) - } - if (file.type?.includes('video')) { - return ( - -
-
-
- ) - } - if (file.type?.includes('audio')) { - return ( - -
-
-
- ) - } - return ( - - -
-
-
{file.name}
-
{file.type?.split('/')[file.type?.split('/').length - 1]} · {file.size}
-
-
handleDelete(file)} - >
-
-
- ) - })} -
-
} +
+ +
{/* Message input area */} void; + wrap?: FlexProps['wrap']; + className?: string; +} + +const FileList: FC = ({ fileList, onDelete, wrap, + className = "rb:mx-3! rb:mt-3! rb:w-max!" + }) => { + if (!fileList.length) return null + + return ( + + {fileList.map((file) => { + if (file.type?.includes('image')) { + return ( + +
+ {file.name} + {onDelete &&
onDelete(file)} + >
} +
+
+ ) + } + if (file.type?.includes('video')) { + return ( + +
+
+
+ ) + } + if (file.type?.includes('audio')) { + return ( + +
+
+
+ ) + } + return ( + + +
+
+
{file.name}
+
{[file.type?.split('/').pop(), file.size].filter(item => item).join(' · ')}
+
+ {onDelete &&
onDelete(file)} + >
} +
+
+ ) + })} +
+ ) +} + +export default FileList diff --git a/web/src/components/CodeMirrorEditor/index.tsx b/web/src/components/CodeMirrorEditor/index.tsx index e100b75b..ec2a6780 100644 --- a/web/src/components/CodeMirrorEditor/index.tsx +++ b/web/src/components/CodeMirrorEditor/index.tsx @@ -6,12 +6,14 @@ */ import { useEffect, useRef, useMemo } from 'react'; import { EditorView, basicSetup } from 'codemirror'; +import { placeholder as cmPlaceholder } from '@codemirror/view'; import { EditorState } from '@codemirror/state'; import { python } from '@codemirror/lang-python'; import { javascript } from '@codemirror/lang-javascript'; import { java } from '@codemirror/lang-java'; import { cpp } from '@codemirror/lang-cpp'; import { rust } from '@codemirror/lang-rust'; +import { json } from '@codemirror/lang-json'; import { oneDark } from '@codemirror/theme-one-dark'; /** @@ -26,12 +28,14 @@ import { oneDark } from '@codemirror/theme-one-dark'; */ interface CodeMirrorEditorProps { value?: string; - language?: 'python' | 'python3' | 'javascript' | 'typescript' | 'java' | 'cpp' | 'c' | 'rust'; + language?: 'python' | 'python3' | 'javascript' | 'typescript' | 'java' | 'cpp' | 'c' | 'rust' | 'json'; onChange?: (value: string) => void; theme?: 'light' | 'dark'; readOnly?: boolean; height?: string; size?: 'default' | 'small'; + placeholder?: string; + variant?: 'outlined' | 'borderless'; } /** @@ -47,6 +51,7 @@ const languageExtensions: Record = { cpp: cpp(), c: cpp(), rust: rust(), + json: json(), }; /** @@ -61,6 +66,8 @@ const CodeMirrorEditor = ({ theme = 'light', readOnly = false, size, + placeholder, + variant = 'borderless', }: CodeMirrorEditorProps) => { // Reference to the DOM element that will contain the editor const editorRef = useRef(null); @@ -88,6 +95,7 @@ const CodeMirrorEditor = ({ } }), EditorState.readOnly.of(readOnly), // Set read-only mode + ...(placeholder ? [cmPlaceholder(placeholder)] : []), ]; // Apply dark theme if specified @@ -111,7 +119,7 @@ const CodeMirrorEditor = ({ return () => { viewRef.current?.destroy(); }; - }, [language, theme, readOnly]); + }, [language, theme, readOnly, placeholder]); /** * Update editor content when the value prop changes externally @@ -144,7 +152,13 @@ const CodeMirrorEditor = ({ return `${size === 'small' ? 16 : 20}px` }, [size]) - return
; + return ( +
+ ); }; export default CodeMirrorEditor; diff --git a/web/src/views/Conversation/components/FileUpload.tsx b/web/src/views/Conversation/components/FileUpload.tsx index 67fff913..0cbef569 100644 --- a/web/src/views/Conversation/components/FileUpload.tsx +++ b/web/src/views/Conversation/components/FileUpload.tsx @@ -237,8 +237,11 @@ const UploadFiles = forwardRef(({ percent: 0, type: rcFile.type, originFileObj: rcFile, - thumbUrl: URL.createObjectURL(rcFile) + thumbUrl: URL.createObjectURL(rcFile), + size: rcFile.size, } + + console.log('fileVo', fileVo) onChange?.(fileVo) request.uploadFile(action, formData, requestConfig) .then(res => { diff --git a/web/src/views/Workflow/components/AddChatVariable/ChatVariableModal.tsx b/web/src/views/Workflow/components/AddChatVariable/ChatVariableModal.tsx index 18495295..69ce73bd 100644 --- a/web/src/views/Workflow/components/AddChatVariable/ChatVariableModal.tsx +++ b/web/src/views/Workflow/components/AddChatVariable/ChatVariableModal.tsx @@ -6,7 +6,7 @@ */ import { forwardRef, useImperativeHandle, useState, useRef, useMemo } from 'react'; import { Form, Input, Select, InputNumber, Button, Row, Col, Flex, Spin } from 'antd'; -import clsx from 'clsx'; +import { PlusOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; import type { ChatVariableModalRef } from './types' @@ -18,9 +18,31 @@ import UploadFileListModal from '@/views/Conversation/components/UploadFileListM import type { UploadFileListModalRef } from '@/views/Conversation/types' import { getFileInfoByUrl } from '@/api/fileStorage' import { transform_file_type } from '@/views/Conversation/components/FileUpload' +import RadioGroupBtn from '../Properties/RadioGroupBtn'; +import CodeMirrorEditor from '@/components/CodeMirrorEditor'; +import FileList from '@/components/Chat/FileList' const FormItem = Form.Item; +const object_placeholder = `# example +# { +# "name": "redbear", +# "age": 2 +# }` + +const array_object_placeholder = `# example +# [ +# { +# "name": "redbear", +# "age": 2 +# }, +# { +# "name": "redbear", +# "age": 2 +# } +# ] +` + interface ChatVariableModalProps { refresh: (value: ChatVariable, editIndex?: number) => void; } @@ -233,7 +255,7 @@ const ChatVariableModal = forwardRef { - form.setFieldValue('defaultValue', undefined); + form.setFieldValue('defaultValue', value === 'array[string]' ? [] : undefined); setFileList([]); if (value === 'file' || value === 'array[file]') form.setFieldsValue(defaultFileUploadValues as any); }} @@ -244,7 +266,8 @@ const ChatVariableModal = forwardRef - {type === 'file' || type === 'array[file]' ? ( + {type?.includes('file') + ? ( <> {previewFileList.length > 0 && ( - - {previewFileList.map((file) => ( - - {file.type?.includes('image') ? ( -
- {file.name} -
handleDelete(file)} - /> -
- ) : ( - -
-
-
{file.name}
-
- {file.type?.split('/').pop()} · {file.size} -
-
-
handleDelete(file)} - /> - - )} - - ))} - + )} - ) : ( + ) + : ['array[string]', 'array[number]', 'array[boolean]'].includes(type) + ? ( + + + {(fields, { add, remove }) => ( + + {fields.map(({ key, name }) => ( + + + {type === 'array[number]' + ? + : type === 'array[boolean]' + ? + : + } + +
remove(name)} + >
+
+ ))} + +
+ )} +
+
+ ) + : ( {type === 'number' ? : type === 'boolean' - ? } diff --git a/web/src/views/Workflow/components/Properties/CaseList/index.tsx b/web/src/views/Workflow/components/Properties/CaseList/index.tsx index 88eace06..5d3d2607 100644 --- a/web/src/views/Workflow/components/Properties/CaseList/index.tsx +++ b/web/src/views/Workflow/components/Properties/CaseList/index.tsx @@ -281,7 +281,7 @@ const CaseList: FC = ({ options={options} size="small" allowClear={false} - onChange={(val) => handleLeftFieldChange(caseIndex, conditionIndex, val)} + onChange={(val) => handleLeftFieldChange(caseIndex, conditionIndex, val as string)} variant="borderless" className="rb:w-36!" /> diff --git a/web/src/views/Workflow/components/Properties/RadioGroupBtn.tsx b/web/src/views/Workflow/components/Properties/RadioGroupBtn.tsx index 0e0166ae..8c475575 100644 --- a/web/src/views/Workflow/components/Properties/RadioGroupBtn.tsx +++ b/web/src/views/Workflow/components/Properties/RadioGroupBtn.tsx @@ -63,6 +63,7 @@ const RadioGroupBtn: FC = ({ allowClear = true, block = false, type, + className, }) => { /** Listen to value changes and trigger side effects via onValueChange callback */ useEffect(() => { @@ -86,7 +87,7 @@ const RadioGroupBtn: FC = ({ } return ( -
+
{/* Render each option as a selectable card */} {options.map(option => (