mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-19 04:15:55 +08:00
file upload
This commit is contained in:
parent
d69b453729
commit
b3529d3ccc
@ -0,0 +1,102 @@
|
|||||||
|
import React, { useCallback, useMemo, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import produce from 'immer'
|
||||||
|
import { RiEqualizer2Line } from '@remixicon/react'
|
||||||
|
import { FolderUpload } from '@/app/components/base/icons/src/vender/features'
|
||||||
|
import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card'
|
||||||
|
import SettingModal from '@/app/components/base/features/new-feature-panel/file-upload/setting-modal'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||||
|
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||||
|
import { FeatureEnum } from '@/app/components/base/features/types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onChange?: OnFeaturesChange
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileUpload = ({
|
||||||
|
onChange,
|
||||||
|
}: Props) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const file = useFeatures(s => s.features.file)
|
||||||
|
const featuresStore = useFeaturesStore()
|
||||||
|
const [modalOpen, setModalOpen] = useState(false)
|
||||||
|
const [isHovering, setIsHovering] = useState(false)
|
||||||
|
|
||||||
|
const supportedTypes = useMemo(() => {
|
||||||
|
return file?.allowed_file_types?.join(',') || '-'
|
||||||
|
}, [file?.allowed_file_types])
|
||||||
|
|
||||||
|
const handleChange = useCallback((type: FeatureEnum, enabled: boolean) => {
|
||||||
|
const {
|
||||||
|
features,
|
||||||
|
setFeatures,
|
||||||
|
} = featuresStore!.getState()
|
||||||
|
|
||||||
|
const newFeatures = produce(features, (draft) => {
|
||||||
|
draft[type] = {
|
||||||
|
...draft[type],
|
||||||
|
enabled,
|
||||||
|
image: { enabled },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setFeatures(newFeatures)
|
||||||
|
if (onChange)
|
||||||
|
onChange(newFeatures)
|
||||||
|
}, [featuresStore, onChange])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FeatureCard
|
||||||
|
icon={
|
||||||
|
<div className='shrink-0 p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-blue-blue-600'>
|
||||||
|
<FolderUpload className='w-4 h-4 text-text-primary-on-surface' />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
title={t('appDebug.feature.fileUpload.title')}
|
||||||
|
value={file?.enabled}
|
||||||
|
onChange={state => handleChange(FeatureEnum.file, state)}
|
||||||
|
onMouseEnter={() => setIsHovering(true)}
|
||||||
|
onMouseLeave={() => setIsHovering(false)}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
{!file?.enabled && (
|
||||||
|
<div className='min-h-8 text-text-tertiary system-xs-regular line-clamp-2'>{t('appDebug.feature.fileUpload.description')}</div>
|
||||||
|
)}
|
||||||
|
{file?.enabled && (
|
||||||
|
<>
|
||||||
|
{!isHovering && !modalOpen && (
|
||||||
|
<div className='pt-0.5 flex items-center gap-4'>
|
||||||
|
<div className=''>
|
||||||
|
<div className='mb-0.5 text-text-tertiary system-2xs-medium-uppercase'>{t('appDebug.feature.fileUpload.supportedTypes')}</div>
|
||||||
|
<div className='text-text-secondary system-xs-regular'>{supportedTypes}</div>
|
||||||
|
</div>
|
||||||
|
<div className='w-px h-[27px] bg-divider-subtle rotate-12'></div>
|
||||||
|
<div className=''>
|
||||||
|
<div className='mb-0.5 text-text-tertiary system-2xs-medium-uppercase'>{t('appDebug.feature.fileUpload.numberLimit')}</div>
|
||||||
|
<div className='text-text-secondary system-xs-regular'>{file?.number_limits}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(isHovering || modalOpen) && (
|
||||||
|
<SettingModal
|
||||||
|
open={modalOpen}
|
||||||
|
onOpen={(v) => {
|
||||||
|
setModalOpen(v)
|
||||||
|
setIsHovering(v)
|
||||||
|
}}
|
||||||
|
onChange={onChange}
|
||||||
|
>
|
||||||
|
<Button className='w-full'>
|
||||||
|
<RiEqualizer2Line className='mr-1 w-4 h-4' />
|
||||||
|
{t('common.operation.settings')}
|
||||||
|
</Button>
|
||||||
|
</SettingModal>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</FeatureCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileUpload
|
@ -0,0 +1,86 @@
|
|||||||
|
import React, { useCallback, useMemo, useState } from 'react'
|
||||||
|
import produce from 'immer'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
|
import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/file-upload-setting'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||||
|
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||||
|
import type { UploadFileSetting } from '@/app/components/workflow/types'
|
||||||
|
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||||
|
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||||
|
|
||||||
|
type SettingContentProps = {
|
||||||
|
onClose: () => void
|
||||||
|
onChange?: OnFeaturesChange
|
||||||
|
}
|
||||||
|
const SettingContent = ({
|
||||||
|
onClose,
|
||||||
|
onChange,
|
||||||
|
}: SettingContentProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const featuresStore = useFeaturesStore()
|
||||||
|
const file = useFeatures(state => state.features.file)
|
||||||
|
const fileSettingPayload = useMemo(() => {
|
||||||
|
return {
|
||||||
|
allowed_file_upload_methods: file?.allowed_file_upload_methods || ['local_file', 'remote_url'],
|
||||||
|
allowed_file_types: file?.allowed_file_types || [SupportUploadFileTypes.image],
|
||||||
|
allowed_file_extensions: file?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image],
|
||||||
|
max_length: file?.number_limits || 3,
|
||||||
|
} as UploadFileSetting
|
||||||
|
}, [file])
|
||||||
|
const [tempPayload, setTempPayload] = useState<UploadFileSetting>(fileSettingPayload)
|
||||||
|
|
||||||
|
const handleChange = useCallback(() => {
|
||||||
|
const {
|
||||||
|
features,
|
||||||
|
setFeatures,
|
||||||
|
} = featuresStore!.getState()
|
||||||
|
|
||||||
|
const newFeatures = produce(features, (draft) => {
|
||||||
|
draft.file = {
|
||||||
|
...draft.file,
|
||||||
|
allowed_file_upload_methods: tempPayload.allowed_file_upload_methods,
|
||||||
|
number_limits: tempPayload.max_length,
|
||||||
|
allowed_file_types: tempPayload.allowed_file_types,
|
||||||
|
allowed_file_extensions: tempPayload.allowed_file_extensions,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setFeatures(newFeatures)
|
||||||
|
if (onChange)
|
||||||
|
onChange(newFeatures)
|
||||||
|
}, [featuresStore, onChange, tempPayload])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='mb-4 flex items-center justify-between'>
|
||||||
|
<div className='text-text-primary system-xl-semibold'>{t('appDebug.feature.fileUpload.modalTitle')}</div>
|
||||||
|
<div className='p-1 cursor-pointer' onClick={onClose}><RiCloseLine className='w-4 h-4 text-text-tertiary'/></div>
|
||||||
|
</div>
|
||||||
|
<FileUploadSetting
|
||||||
|
isMultiple
|
||||||
|
inFeaturePanel
|
||||||
|
payload={tempPayload}
|
||||||
|
onChange={(p: UploadFileSetting) => setTempPayload(p)}
|
||||||
|
/>
|
||||||
|
<div className='mt-4 flex items-center justify-end'>
|
||||||
|
<Button
|
||||||
|
onClick={onClose}
|
||||||
|
className='mr-2'
|
||||||
|
>
|
||||||
|
{t('common.operation.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant='primary'
|
||||||
|
onClick={handleChange}
|
||||||
|
disabled={tempPayload.allowed_file_types.length === 0}
|
||||||
|
>
|
||||||
|
{t('common.operation.save')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(SettingContent)
|
@ -0,0 +1,50 @@
|
|||||||
|
'use client'
|
||||||
|
import { memo } from 'react'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import SettingContent from '@/app/components/base/features/new-feature-panel/file-upload/setting-content'
|
||||||
|
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||||
|
|
||||||
|
type FileUploadSettingsProps = {
|
||||||
|
open: boolean
|
||||||
|
onOpen: (state: any) => void
|
||||||
|
onChange?: OnFeaturesChange
|
||||||
|
disabled?: boolean
|
||||||
|
children?: React.ReactNode
|
||||||
|
}
|
||||||
|
const FileUploadSettings = ({
|
||||||
|
open,
|
||||||
|
onOpen,
|
||||||
|
onChange,
|
||||||
|
disabled,
|
||||||
|
children,
|
||||||
|
}: FileUploadSettingsProps) => {
|
||||||
|
return (
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={open}
|
||||||
|
onOpenChange={onOpen}
|
||||||
|
placement='left'
|
||||||
|
offset={{
|
||||||
|
mainAxis: 32,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger className='flex' onClick={() => !disabled && onOpen((open: boolean) => !open)}>
|
||||||
|
{children}
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent style={{ zIndex: 50 }}>
|
||||||
|
<div className='w-[360px] p-4 bg-components-panel-bg rounded-2xl border-[0.5px] border-components-panel-border shadow-2xl'>
|
||||||
|
<SettingContent
|
||||||
|
onClose={() => onOpen(false)}
|
||||||
|
onChange={(v) => {
|
||||||
|
onChange?.(v)
|
||||||
|
onOpen(false)
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default memo(FileUploadSettings)
|
@ -7,10 +7,7 @@ import {
|
|||||||
RiEqualizer2Line,
|
RiEqualizer2Line,
|
||||||
RiExternalLinkLine,
|
RiExternalLinkLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import {
|
import { MessageFast } from '@/app/components/base/icons/src/vender/features'
|
||||||
FolderUpload,
|
|
||||||
MessageFast,
|
|
||||||
} from '@/app/components/base/icons/src/vender/features'
|
|
||||||
import DialogWrapper from '@/app/components/base/features/new-feature-panel/dialog-wrapper'
|
import DialogWrapper from '@/app/components/base/features/new-feature-panel/dialog-wrapper'
|
||||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
@ -25,6 +22,7 @@ import ConversationOpener from '@/app/components/base/features/new-feature-panel
|
|||||||
import Moderation from '@/app/components/base/features/new-feature-panel/moderation'
|
import Moderation from '@/app/components/base/features/new-feature-panel/moderation'
|
||||||
import SpeechToText from '@/app/components/base/features/new-feature-panel/speech-to-text'
|
import SpeechToText from '@/app/components/base/features/new-feature-panel/speech-to-text'
|
||||||
import TextToSpeech from '@/app/components/base/features/new-feature-panel/text-to-speech'
|
import TextToSpeech from '@/app/components/base/features/new-feature-panel/text-to-speech'
|
||||||
|
import FileUpload from '@/app/components/base/features/new-feature-panel/file-upload'
|
||||||
import FollowUp from '@/app/components/base/features/new-feature-panel/follow-up'
|
import FollowUp from '@/app/components/base/features/new-feature-panel/follow-up'
|
||||||
import Citation from '@/app/components/base/features/new-feature-panel/citation'
|
import Citation from '@/app/components/base/features/new-feature-panel/citation'
|
||||||
|
|
||||||
@ -100,18 +98,7 @@ const NewFeaturePanel = ({
|
|||||||
{text2speechDefaultModel && (
|
{text2speechDefaultModel && (
|
||||||
<TextToSpeech onChange={onChange} />
|
<TextToSpeech onChange={onChange} />
|
||||||
)}
|
)}
|
||||||
{/* file upload ##TODO## */}
|
<FileUpload onChange={onChange} />
|
||||||
<div className='group mb-1 p-3 border-t-[0.5px] border-l-[0.5px] border-effects-highlight rounded-xl bg-background-section-burn'>
|
|
||||||
<div className='mb-2 flex items-center gap-2'>
|
|
||||||
<div className='shrink-0 p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-blue-blue-600'>
|
|
||||||
<FolderUpload className='w-4 h-4 text-text-primary-on-surface' />
|
|
||||||
</div>
|
|
||||||
<div className='grow flex items-center text-text-secondary system-sm-semibold'>
|
|
||||||
File upload
|
|
||||||
</div>
|
|
||||||
<Switch className='shrink-0' onChange={value => handleChange(FeatureEnum.text2speech, value)} defaultValue={!!features.text2speech?.enabled} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isChatMode && (
|
{isChatMode && (
|
||||||
<FollowUp onChange={onChange} />
|
<FollowUp onChange={onChange} />
|
||||||
)}
|
)}
|
||||||
|
@ -33,7 +33,11 @@ export type FileUpload = {
|
|||||||
number_limits?: number
|
number_limits?: number
|
||||||
transfer_methods?: TransferMethod[]
|
transfer_methods?: TransferMethod[]
|
||||||
}
|
}
|
||||||
}
|
allowed_file_types?: string[]
|
||||||
|
allowed_file_extensions?: string[]
|
||||||
|
allowed_file_upload_methods?: string[]
|
||||||
|
number_limits?: number
|
||||||
|
} & EnabledOrDisabled
|
||||||
|
|
||||||
export enum FeatureEnum {
|
export enum FeatureEnum {
|
||||||
moreLikeThis = 'moreLikeThis',
|
moreLikeThis = 'moreLikeThis',
|
||||||
|
@ -35,6 +35,7 @@ import type {
|
|||||||
} from './types'
|
} from './types'
|
||||||
import {
|
import {
|
||||||
ControlMode,
|
ControlMode,
|
||||||
|
SupportUploadFileTypes,
|
||||||
} from './types'
|
} from './types'
|
||||||
import { WorkflowContextProvider } from './context'
|
import { WorkflowContextProvider } from './context'
|
||||||
import {
|
import {
|
||||||
@ -88,6 +89,7 @@ import type { Features as FeaturesData } from '@/app/components/base/features/ty
|
|||||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
|
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
[CUSTOM_NODE]: CustomNode,
|
[CUSTOM_NODE]: CustomNode,
|
||||||
@ -404,6 +406,11 @@ const WorkflowWrap = memo(() => {
|
|||||||
number_limits: features.file_upload?.image.number_limits || 3,
|
number_limits: features.file_upload?.image.number_limits || 3,
|
||||||
transfer_methods: features.file_upload?.image.transfer_methods || ['local_file', 'remote_url'],
|
transfer_methods: features.file_upload?.image.transfer_methods || ['local_file', 'remote_url'],
|
||||||
},
|
},
|
||||||
|
enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
|
||||||
|
allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
|
||||||
|
allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
|
||||||
|
allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image.transfer_methods || ['local_file', 'remote_url'],
|
||||||
|
number_limits: features.file_upload?.number_limits || features.file_upload?.image.number_limits || 3,
|
||||||
},
|
},
|
||||||
opening: {
|
opening: {
|
||||||
enabled: !!features.opening_statement,
|
enabled: !!features.opening_statement,
|
||||||
|
@ -14,12 +14,14 @@ import { TransferMethod } from '@/types/app'
|
|||||||
type Props = {
|
type Props = {
|
||||||
payload: UploadFileSetting
|
payload: UploadFileSetting
|
||||||
isMultiple: boolean
|
isMultiple: boolean
|
||||||
|
inFeaturePanel?: boolean
|
||||||
onChange: (payload: UploadFileSetting) => void
|
onChange: (payload: UploadFileSetting) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileUploadSetting: FC<Props> = ({
|
const FileUploadSetting: FC<Props> = ({
|
||||||
payload,
|
payload,
|
||||||
isMultiple,
|
isMultiple,
|
||||||
|
inFeaturePanel = false,
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -83,29 +85,31 @@ const FileUploadSetting: FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Field
|
{!inFeaturePanel && (
|
||||||
title={t('appDebug.variableConig.file.supportFileTypes')}
|
<Field
|
||||||
>
|
title={t('appDebug.variableConig.file.supportFileTypes')}
|
||||||
<div className='space-y-1'>
|
>
|
||||||
{
|
<div className='space-y-1'>
|
||||||
[SupportUploadFileTypes.image, SupportUploadFileTypes.document, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
|
{
|
||||||
<FileTypeItem
|
[SupportUploadFileTypes.image, SupportUploadFileTypes.document, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
|
||||||
key={type}
|
<FileTypeItem
|
||||||
type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
|
key={type}
|
||||||
selected={allowed_file_types.includes(type)}
|
type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
|
||||||
onToggle={handleSupportFileTypeChange}
|
selected={allowed_file_types.includes(type)}
|
||||||
/>
|
onToggle={handleSupportFileTypeChange}
|
||||||
))
|
/>
|
||||||
}
|
))
|
||||||
<FileTypeItem
|
}
|
||||||
type={SupportUploadFileTypes.custom}
|
<FileTypeItem
|
||||||
selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
|
type={SupportUploadFileTypes.custom}
|
||||||
onToggle={handleSupportFileTypeChange}
|
selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
|
||||||
customFileTypes={allowed_file_extensions}
|
onToggle={handleSupportFileTypeChange}
|
||||||
onCustomFileTypesChange={handleCustomFileTypesChange}
|
customFileTypes={allowed_file_extensions}
|
||||||
/>
|
onCustomFileTypesChange={handleCustomFileTypesChange}
|
||||||
</div>
|
/>
|
||||||
</Field>
|
</div>
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
<Field
|
<Field
|
||||||
title='Upload File Types'
|
title='Upload File Types'
|
||||||
className='mt-4'
|
className='mt-4'
|
||||||
@ -144,6 +148,32 @@ const FileUploadSetting: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
|
{inFeaturePanel && (
|
||||||
|
<Field
|
||||||
|
title={t('appDebug.variableConig.file.supportFileTypes')}
|
||||||
|
className='mt-4'
|
||||||
|
>
|
||||||
|
<div className='space-y-1'>
|
||||||
|
{
|
||||||
|
[SupportUploadFileTypes.image, SupportUploadFileTypes.document, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
|
||||||
|
<FileTypeItem
|
||||||
|
key={type}
|
||||||
|
type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
|
||||||
|
selected={allowed_file_types.includes(type)}
|
||||||
|
onToggle={handleSupportFileTypeChange}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
<FileTypeItem
|
||||||
|
type={SupportUploadFileTypes.custom}
|
||||||
|
selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
|
||||||
|
onToggle={handleSupportFileTypeChange}
|
||||||
|
customFileTypes={allowed_file_extensions}
|
||||||
|
onCustomFileTypesChange={handleCustomFileTypesChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -199,6 +199,13 @@ const translation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
fileUpload: {
|
||||||
|
title: 'File Upload',
|
||||||
|
description: 'The chat input box allows uploading of images, documents, and other files.',
|
||||||
|
supportedTypes: 'Support File Types',
|
||||||
|
numberLimit: 'Max uploads',
|
||||||
|
modalTitle: 'File Upload Setting',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
generate: {
|
generate: {
|
||||||
title: 'Prompt Generator',
|
title: 'Prompt Generator',
|
||||||
|
@ -199,6 +199,13 @@ const translation = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
fileUpload: {
|
||||||
|
title: '文件上传',
|
||||||
|
description: '聊天输入框支持上传文件。类型包括图片、文档以及其它类型',
|
||||||
|
supportedTypes: '支持的文件类型',
|
||||||
|
numberLimit: '最大上传数',
|
||||||
|
modalTitle: '文件上传设置',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
generate: {
|
generate: {
|
||||||
title: '提示词生成器',
|
title: '提示词生成器',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user