From 0be99ad01ca23832cab0ca1b09a40268f4335489 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 2 Aug 2024 18:17:04 +0800 Subject: [PATCH] feat: select file types --- .../config-var/config-modal/index.tsx | 16 ++- .../base/prompt-editor/constants.tsx | 9 +- .../nodes/_base/components/file-type-item.tsx | 48 +++++++++ .../_base/components/file-upload-setting.tsx | 98 +++++++++++++++++++ web/app/components/workflow/types.ts | 17 +++- web/i18n/en-US/app-debug.ts | 14 +++ web/i18n/zh-Hans/app-debug.ts | 14 +++ 7 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 web/app/components/workflow/nodes/_base/components/file-type-item.tsx create mode 100644 web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx index 6a910ee4db..045860f9b8 100644 --- a/web/app/components/app/configuration/config-var/config-modal/index.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx @@ -11,10 +11,11 @@ import Field from './field' import Toast from '@/app/components/base/toast' import { checkKeys, getNewVarInWorkflow } from '@/utils/var' import ConfigContext from '@/context/debug-configuration' -import type { InputVar, MoreInfo } from '@/app/components/workflow/types' +import type { InputVar, MoreInfo, UploadFileSetting } from '@/app/components/workflow/types' import Modal from '@/app/components/base/modal' import Switch from '@/app/components/base/switch' import { ChangeType, InputVarType } from '@/app/components/workflow/types' +import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/file-upload-setting' const TEXT_MAX_LENGTH = 256 @@ -98,7 +99,7 @@ const ConfigModal: FC = ({ if (isStringInput || type === InputVarType.number) { onConfirm(tempPayload, moreInfo) } - else { + else if (type === InputVarType.select) { if (options?.length === 0) { Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.atLeastOneOption') }) return @@ -118,6 +119,9 @@ const ConfigModal: FC = ({ } onConfirm(tempPayload, moreInfo) } + else { + onConfirm(tempPayload, moreInfo) + } } return ( @@ -174,6 +178,14 @@ const ConfigModal: FC = ({ )} + {[InputVarType.singleFile, InputVarType.multiFiles].includes(type) && ( + setTempPayload(p as InputVar)} + isMultiple={type === InputVarType.multiFiles} + /> + )} + diff --git a/web/app/components/base/prompt-editor/constants.tsx b/web/app/components/base/prompt-editor/constants.tsx index e02963128a..0caf45ee7d 100644 --- a/web/app/components/base/prompt-editor/constants.tsx +++ b/web/app/components/base/prompt-editor/constants.tsx @@ -1,4 +1,4 @@ -import type { ValueSelector } from '../../workflow/types' +import { SupportUploadFileTypes, type ValueSelector } from '../../workflow/types' export const CONTEXT_PLACEHOLDER_TEXT = '{{#context#}}' export const HISTORY_PLACEHOLDER_TEXT = '{{#histories#}}' @@ -49,3 +49,10 @@ export const getInputVars = (text: string): ValueSelector[] => { } return [] } + +export const FILE_EXTS = { + [SupportUploadFileTypes.image]: ['JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'], + [SupportUploadFileTypes.document]: ['TXT', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'], + [SupportUploadFileTypes.audio]: ['MP3', 'M4A', 'WAV', 'WEBM', 'AMR'], + [SupportUploadFileTypes.video]: ['MP4', 'MOV', 'MPEG', 'MPGA'], +} diff --git a/web/app/components/workflow/nodes/_base/components/file-type-item.tsx b/web/app/components/workflow/nodes/_base/components/file-type-item.tsx new file mode 100644 index 0000000000..43d85ad2f4 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/file-type-item.tsx @@ -0,0 +1,48 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import type { SupportUploadFileTypes } from '../../../types' +import cn from '@/utils/classnames' +import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' + +type Props = { + type: SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video + selected: boolean + onSelect: (type: SupportUploadFileTypes) => void +} + +const FileTypeItem: FC = ({ + type, + selected, + onSelect, +}) => { + const { t } = useTranslation() + + const handleOnSelect = useCallback(() => { + if (!selected) + onSelect(type) + }, [selected, onSelect, type]) + + return ( +
+
+ {/* TODO: Wait File type icon */} + +
+
{t(`appDebug.variableConig.file.${type}.name`)}
+
{FILE_EXTS[type].join(', ')}
+
+
+
+ ) +} + +export default React.memo(FileTypeItem) diff --git a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx new file mode 100644 index 0000000000..1e062b7eb7 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx @@ -0,0 +1,98 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import produce from 'immer' +import type { UploadFileSetting } from '../../../types' +import { SupportUploadFileTypes } from '../../../types' +import OptionCard from './option-card' +import FileTypeItem from './file-type-item' +import Field from '@/app/components/app/configuration/config-var/config-modal/field' +import { TransferMethod } from '@/types/app' + +type Props = { + payload: UploadFileSetting + isMultiple: boolean + onChange: (payload: UploadFileSetting) => void +} + +const FileUploadSetting: FC = ({ + payload, + isMultiple, + onChange, +}) => { + const { + uploadMethod, + maxUploadNumLimit, + supportFileTypes, + customFileTypes, + } = payload + + const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => { + const newPayload = produce(payload, (draft) => { + draft.supportFileTypes = type + }) + onChange(newPayload) + }, [onChange, payload]) + + const handleUploadMethodChange = useCallback((method: TransferMethod) => { + return () => { + const newPayload = produce(payload, (draft) => { + draft.uploadMethod = method + }) + onChange(newPayload) + } + }, [onChange, payload]) + + return ( +
+ +
+ { + [SupportUploadFileTypes.image, SupportUploadFileTypes.document, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => ( + + )) + } +
+
+ +
+ + + +
+
+ {isMultiple && ( + +
+ Max number of uploads +
+
+ )} + +
+ ) +} +export default React.memo(FileUploadSetting) diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 7c411b4cdc..73042b94ae 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -144,7 +144,7 @@ export type InputVar = { hint?: string options?: string[] value_selector?: ValueSelector -} +} & Partial export type ModelConfig = { provider: string @@ -329,3 +329,18 @@ export type MoreInfo = { export type ToolWithProvider = Collection & { tools: Tool[] } + +export enum SupportUploadFileTypes { + image = 'image', + document = 'document', + audio = 'audio', + video = 'video', + custom = 'custom', +} + +export type UploadFileSetting = { + uploadMethod: TransferMethod + maxUploadNumLimit?: number // multiple files upload limit + supportFileTypes: SupportUploadFileTypes + customFileTypes?: string[] +} diff --git a/web/i18n/en-US/app-debug.ts b/web/i18n/en-US/app-debug.ts index aa0adf1301..85895f09de 100644 --- a/web/i18n/en-US/app-debug.ts +++ b/web/i18n/en-US/app-debug.ts @@ -324,6 +324,20 @@ const translation = { 'inputPlaceholder': 'Please input', 'content': 'Content', 'required': 'Required', + 'file': { + image: { + name: 'Image', + }, + audio: { + name: 'Audio', + }, + document: { + name: 'Document', + }, + video: { + name: 'Video', + }, + }, 'errorMsg': { varNameRequired: 'Variable name is required', labelNameRequired: 'Label name is required', diff --git a/web/i18n/zh-Hans/app-debug.ts b/web/i18n/zh-Hans/app-debug.ts index 24aff2142e..60515d64a3 100644 --- a/web/i18n/zh-Hans/app-debug.ts +++ b/web/i18n/zh-Hans/app-debug.ts @@ -320,6 +320,20 @@ const translation = { 'inputPlaceholder': '请输入', 'labelName': '显示名称', 'required': '必填', + 'file': { + image: { + name: '图片', + }, + audio: { + name: '音频', + }, + document: { + name: '文档', + }, + video: { + name: '视频', + }, + }, 'content': '内容', 'errorMsg': { varNameRequired: '变量名称必填',