(null)
+
+ useEffect(() => {
+ if (error)
+ throw error
+ }, [error])
+
+ return setError
+}
+
+// Hook for catching async errors
+export function useAsyncError() {
+ const [, setError] = useState()
+
+ return useCallback(
+ (error: Error) => {
+ setError(() => {
+ throw error
+ })
+ },
+ [setError],
+ )
+}
+
+// HOC for wrapping components with error boundary
+export function withErrorBoundary(
+ Component: React.ComponentType
,
+ errorBoundaryProps?: Omit,
+): React.ComponentType {
+ const WrappedComponent = (props: P) => (
+
+
+
+ )
+
+ WrappedComponent.displayName = `withErrorBoundary(${Component.displayName || Component.name || 'Component'})`
+
+ return WrappedComponent
+}
+
+// Simple error fallback component
+export const ErrorFallback: React.FC<{
+ error: Error
+ resetErrorBoundary: () => void
+}> = ({ error, resetErrorBoundary }) => {
+ return (
+
+
Oops! Something went wrong
+
{error.message}
+
+
+ )
+}
+
+export default ErrorBoundary
diff --git a/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx b/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx
index 095137203b..ff45a7ea4c 100644
--- a/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx
+++ b/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx
@@ -26,6 +26,7 @@ import { CustomConfigurationStatusEnum } from '@/app/components/header/account-s
import cn from '@/utils/classnames'
import { noop } from 'lodash-es'
import { useDocLink } from '@/context/i18n'
+import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
const systemTypes = ['openai_moderation', 'keywords', 'api']
@@ -55,7 +56,7 @@ const ModerationSettingModal: FC = ({
const { setShowAccountSettingModal } = useModalContext()
const handleOpenSettingsModal = () => {
setShowAccountSettingModal({
- payload: 'provider',
+ payload: ACCOUNT_SETTING_TAB.PROVIDER,
onCancelCallback: () => {
mutate()
},
diff --git a/web/app/components/base/file-uploader/hooks.ts b/web/app/components/base/file-uploader/hooks.ts
index 9675123fe7..521ecdbafd 100644
--- a/web/app/components/base/file-uploader/hooks.ts
+++ b/web/app/components/base/file-uploader/hooks.ts
@@ -305,9 +305,23 @@ export const useFile = (fileConfig: FileUpload) => {
const text = e.clipboardData?.getData('text/plain')
if (file && !text) {
e.preventDefault()
+
+ const allowedFileTypes = fileConfig.allowed_file_types || []
+ const fileType = getSupportFileType(file.name, file.type, allowedFileTypes?.includes(SupportUploadFileTypes.custom))
+ const isFileTypeAllowed = allowedFileTypes.includes(fileType)
+
+ // Check if file type is in allowed list
+ if (!isFileTypeAllowed || !fileConfig.enabled) {
+ notify({
+ type: 'error',
+ message: t('common.fileUploader.fileExtensionNotSupport'),
+ })
+ return
+ }
+
handleLocalFileUpload(file)
}
- }, [handleLocalFileUpload])
+ }, [handleLocalFileUpload, fileConfig, notify, t])
const [isDragActive, setIsDragActive] = useState(false)
const handleDragFileEnter = useCallback((e: React.DragEvent) => {
diff --git a/web/app/components/base/form/components/base/base-field.tsx b/web/app/components/base/form/components/base/base-field.tsx
index bf415e08a8..db57059b82 100644
--- a/web/app/components/base/form/components/base/base-field.tsx
+++ b/web/app/components/base/form/components/base/base-field.tsx
@@ -1,20 +1,71 @@
+import CheckboxList from '@/app/components/base/checkbox-list'
+import type { FieldState, FormSchema, TypeWithI18N } from '@/app/components/base/form/types'
+import { FormItemValidateStatusEnum, FormTypeEnum } from '@/app/components/base/form/types'
+import Input from '@/app/components/base/input'
+import Radio from '@/app/components/base/radio'
+import RadioE from '@/app/components/base/radio/ui'
+import PureSelect from '@/app/components/base/select/pure'
+import Tooltip from '@/app/components/base/tooltip'
+import { useRenderI18nObject } from '@/hooks/use-i18n'
+import { useTriggerPluginDynamicOptions } from '@/service/use-triggers'
+import cn from '@/utils/classnames'
+import { RiExternalLinkLine } from '@remixicon/react'
+import type { AnyFieldApi } from '@tanstack/react-form'
+import { useStore } from '@tanstack/react-form'
import {
isValidElement,
memo,
useCallback,
useMemo,
} from 'react'
-import { RiExternalLinkLine } from '@remixicon/react'
-import type { AnyFieldApi } from '@tanstack/react-form'
-import { useStore } from '@tanstack/react-form'
-import cn from '@/utils/classnames'
-import Input from '@/app/components/base/input'
-import PureSelect from '@/app/components/base/select/pure'
-import type { FormSchema } from '@/app/components/base/form/types'
-import { FormTypeEnum } from '@/app/components/base/form/types'
-import { useRenderI18nObject } from '@/hooks/use-i18n'
-import Radio from '@/app/components/base/radio'
-import RadioE from '@/app/components/base/radio/ui'
+import { useTranslation } from 'react-i18next'
+
+const getExtraProps = (type: FormTypeEnum) => {
+ switch (type) {
+ case FormTypeEnum.secretInput:
+ return { type: 'password', autoComplete: 'new-password' }
+ case FormTypeEnum.textNumber:
+ return { type: 'number' }
+ default:
+ return { type: 'text' }
+ }
+}
+
+const getTranslatedContent = ({ content, render }: {
+ content: React.ReactNode | string | null | undefined | TypeWithI18N | Record
+ render: (content: TypeWithI18N | Record) => string
+}): string => {
+ if (isValidElement(content) || typeof content === 'string')
+ return content as string
+
+ if (typeof content === 'object' && content !== null)
+ return render(content as TypeWithI18N)
+
+ return ''
+}
+
+const VALIDATE_STATUS_STYLE_MAP: Record = {
+ [FormItemValidateStatusEnum.Error]: {
+ componentClassName: 'border-components-input-border-destructive focus:border-components-input-border-destructive',
+ textClassName: 'text-text-destructive',
+ infoFieldName: 'errors',
+ },
+ [FormItemValidateStatusEnum.Warning]: {
+ componentClassName: 'border-components-input-border-warning focus:border-components-input-border-warning',
+ textClassName: 'text-text-warning',
+ infoFieldName: 'warnings',
+ },
+ [FormItemValidateStatusEnum.Success]: {
+ componentClassName: '',
+ textClassName: '',
+ infoFieldName: '',
+ },
+ [FormItemValidateStatusEnum.Validating]: {
+ componentClassName: '',
+ textClassName: '',
+ infoFieldName: '',
+ },
+}
export type BaseFieldProps = {
fieldClassName?: string
@@ -25,7 +76,9 @@ export type BaseFieldProps = {
field: AnyFieldApi
disabled?: boolean
onChange?: (field: string, value: any) => void
+ fieldState?: FieldState
}
+
const BaseField = ({
fieldClassName,
labelClassName,
@@ -35,204 +88,259 @@ const BaseField = ({
field,
disabled: propsDisabled,
onChange,
+ fieldState,
}: BaseFieldProps) => {
const renderI18nObject = useRenderI18nObject()
+ const { t } = useTranslation()
const {
+ name,
label,
required,
placeholder,
options,
labelClassName: formLabelClassName,
disabled: formSchemaDisabled,
+ type: formItemType,
+ dynamicSelectParams,
+ multiple = false,
+ tooltip,
+ showCopy,
+ description,
+ url,
+ help,
} = formSchema
const disabled = propsDisabled || formSchemaDisabled
- const memorizedLabel = useMemo(() => {
- if (isValidElement(label))
- return label
+ const [translatedLabel, translatedPlaceholder, translatedTooltip, translatedDescription, translatedHelp] = useMemo(() => {
+ const results = [
+ label,
+ placeholder,
+ tooltip,
+ description,
+ help,
+ ].map(v => getTranslatedContent({ content: v, render: renderI18nObject }))
+ if (!results[1]) results[1] = t('common.placeholder.input')
+ return results
+ }, [label, placeholder, tooltip, description, help, renderI18nObject])
- if (typeof label === 'string')
- return label
+ const watchedVariables = useMemo(() => {
+ const variables = new Set()
- if (typeof label === 'object' && label !== null)
- return renderI18nObject(label as Record)
- }, [label, renderI18nObject])
- const memorizedPlaceholder = useMemo(() => {
- if (typeof placeholder === 'string')
- return placeholder
+ for (const option of options || []) {
+ for (const condition of option.show_on || [])
+ variables.add(condition.variable)
+ }
- if (typeof placeholder === 'object' && placeholder !== null)
- return renderI18nObject(placeholder as Record)
- }, [placeholder, renderI18nObject])
- const optionValues = useStore(field.form.store, (s) => {
+ return Array.from(variables)
+ }, [options])
+
+ const watchedValues = useStore(field.form.store, (s) => {
const result: Record = {}
- options?.forEach((option) => {
- if (option.show_on?.length) {
- option.show_on.forEach((condition) => {
- result[condition.variable] = s.values[condition.variable]
- })
- }
- })
+ for (const variable of watchedVariables)
+ result[variable] = s.values[variable]
+
return result
})
+
const memorizedOptions = useMemo(() => {
return options?.filter((option) => {
- if (!option.show_on || option.show_on.length === 0)
+ if (!option.show_on?.length)
return true
return option.show_on.every((condition) => {
- const conditionValue = optionValues[condition.variable]
- return conditionValue === condition.value
+ return watchedValues[condition.variable] === condition.value
})
}).map((option) => {
return {
- label: typeof option.label === 'string' ? option.label : renderI18nObject(option.label),
+ label: getTranslatedContent({ content: option.label, render: renderI18nObject }),
value: option.value,
}
}) || []
- }, [options, renderI18nObject, optionValues])
+ }, [options, renderI18nObject, watchedValues])
+
const value = useStore(field.form.store, s => s.values[field.name])
+ const { data: dynamicOptionsData, isLoading: isDynamicOptionsLoading, error: dynamicOptionsError } = useTriggerPluginDynamicOptions(
+ dynamicSelectParams || {
+ plugin_id: '',
+ provider: '',
+ action: '',
+ parameter: '',
+ credential_id: '',
+ },
+ formItemType === FormTypeEnum.dynamicSelect,
+ )
+
+ const dynamicOptions = useMemo(() => {
+ if (!dynamicOptionsData?.options)
+ return []
+ return dynamicOptionsData.options.map(option => ({
+ label: getTranslatedContent({ content: option.label, render: renderI18nObject }),
+ value: option.value,
+ }))
+ }, [dynamicOptionsData, renderI18nObject])
+
const handleChange = useCallback((value: any) => {
field.handleChange(value)
onChange?.(field.name, value)
}, [field, onChange])
return (
-
-
- {memorizedLabel}
- {
- required && !isValidElement(label) && (
- *
- )
- }
-
-
- {
- formSchema.type === FormTypeEnum.textInput && (
-
{
- handleChange(e.target.value)
- }}
- onBlur={field.handleBlur}
- disabled={disabled}
- placeholder={memorizedPlaceholder}
+ <>
+
+
+ {
+ [FormTypeEnum.textInput, FormTypeEnum.secretInput, FormTypeEnum.textNumber].includes(formItemType) && (
+
{
+ handleChange(e.target.value)
+ }}
+ onBlur={field.handleBlur}
+ disabled={disabled}
+ placeholder={translatedPlaceholder}
+ {...getExtraProps(formItemType)}
+ showCopyIcon={showCopy}
+ />
+ )
+ }
+ {
+ formItemType === FormTypeEnum.select && !multiple && (
+
handleChange(v)}
+ disabled={disabled}
+ placeholder={translatedPlaceholder}
+ options={memorizedOptions}
+ triggerPopupSameWidth
+ popupProps={{
+ className: 'max-h-[320px] overflow-y-auto',
+ }}
+ />
+ )
+ }
+ {
+ formItemType === FormTypeEnum.checkbox /* && multiple */ && (
+ field.handleChange(v)}
+ options={memorizedOptions}
+ maxHeight='200px'
+ />
+ )
+ }
+ {
+ formItemType === FormTypeEnum.dynamicSelect && (
+
+ )
+ }
+ {
+ formItemType === FormTypeEnum.radio && (
+
+ {
+ memorizedOptions.map(option => (
+
!disabled && handleChange(option.value)}
+ >
+ {
+ formSchema.showRadioUI && (
+
+ )
+ }
+ {option.label}
+
+ ))
+ }
+
+ )
+ }
+ {
+ formItemType === FormTypeEnum.boolean && (
+ field.handleChange(v)}
+ >
+ True
+ False
+
+ )
+ }
+ {fieldState?.validateStatus && [FormItemValidateStatusEnum.Error, FormItemValidateStatusEnum.Warning].includes(fieldState?.validateStatus) && (
- {
- memorizedOptions.map(option => (
-
!disabled && handleChange(option.value)}
- >
- {
- formSchema.showRadioUI && (
-
- )
- }
- {option.label}
-
- ))
- }
+ {fieldState?.[VALIDATE_STATUS_STYLE_MAP[fieldState?.validateStatus].infoFieldName as keyof FieldState]}
- )
- }
- {
- formSchema.type === FormTypeEnum.boolean && (
- field.handleChange(v)}
- >
- True
- False
-
- )
- }
- {
- formSchema.url && (
-
-
- {renderI18nObject(formSchema?.help as any)}
-
- {
-
- }
-
- )
- }
+ )}
+
-
+ {description && (
+
+ {translatedDescription}
+
+ )}
+ {
+ url && (
+
+
+ {translatedHelp}
+
+
+
+ )
+ }
+ >
+
)
}
diff --git a/web/app/components/base/form/components/base/base-form.tsx b/web/app/components/base/form/components/base/base-form.tsx
index 6b7e992510..0d35380523 100644
--- a/web/app/components/base/form/components/base/base-form.tsx
+++ b/web/app/components/base/form/components/base/base-form.tsx
@@ -3,6 +3,7 @@ import {
useCallback,
useImperativeHandle,
useMemo,
+ useState,
} from 'react'
import type {
AnyFieldApi,
@@ -12,9 +13,12 @@ import {
useForm,
useStore,
} from '@tanstack/react-form'
-import type {
- FormRef,
- FormSchema,
+import {
+ type FieldState,
+ FormItemValidateStatusEnum,
+ type FormRef,
+ type FormSchema,
+ type SetFieldsParam,
} from '@/app/components/base/form/types'
import {
BaseField,
@@ -36,6 +40,8 @@ export type BaseFormProps = {
disabled?: boolean
formFromProps?: AnyFormApi
onChange?: (field: string, value: any) => void
+ onSubmit?: (e: React.FormEvent) => void
+ preventDefaultSubmit?: boolean
} & Pick
const BaseForm = ({
@@ -50,6 +56,8 @@ const BaseForm = ({
disabled,
formFromProps,
onChange,
+ onSubmit,
+ preventDefaultSubmit = false,
}: BaseFormProps) => {
const initialDefaultValues = useMemo(() => {
if (defaultValues)
@@ -68,6 +76,8 @@ const BaseForm = ({
const { getFormValues } = useGetFormValues(form, formSchemas)
const { getValidators } = useGetValidators()
+ const [fieldStates, setFieldStates] = useState>({})
+
const showOnValues = useStore(form.store, (s: any) => {
const result: Record = {}
formSchemas.forEach((schema) => {
@@ -81,6 +91,34 @@ const BaseForm = ({
return result
})
+ const setFields = useCallback((fields: SetFieldsParam[]) => {
+ const newFieldStates: Record = { ...fieldStates }
+
+ for (const field of fields) {
+ const { name, value, errors, warnings, validateStatus, help } = field
+
+ if (value !== undefined)
+ form.setFieldValue(name, value)
+
+ let finalValidateStatus = validateStatus
+ if (!finalValidateStatus) {
+ if (errors && errors.length > 0)
+ finalValidateStatus = FormItemValidateStatusEnum.Error
+ else if (warnings && warnings.length > 0)
+ finalValidateStatus = FormItemValidateStatusEnum.Warning
+ }
+
+ newFieldStates[name] = {
+ validateStatus: finalValidateStatus,
+ help,
+ errors,
+ warnings,
+ }
+ }
+
+ setFieldStates(newFieldStates)
+ }, [form, fieldStates])
+
useImperativeHandle(ref, () => {
return {
getForm() {
@@ -89,8 +127,9 @@ const BaseForm = ({
getFormValues: (option) => {
return getFormValues(option)
},
+ setFields,
}
- }, [form, getFormValues])
+ }, [form, getFormValues, setFields])
const renderField = useCallback((field: AnyFieldApi) => {
const formSchema = formSchemas?.find(schema => schema.name === field.name)
@@ -100,18 +139,19 @@ const BaseForm = ({
)
}
return null
- }, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName, disabled, onChange])
+ }, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName, disabled, onChange, fieldStates])
const renderFieldWrapper = useCallback((formSchema: FormSchema) => {
const validators = getValidators(formSchema)
@@ -142,9 +182,18 @@ const BaseForm = ({
if (!formSchemas?.length)
return null
+ const handleSubmit = (e: React.FormEvent) => {
+ if (preventDefaultSubmit) {
+ e.preventDefault()
+ e.stopPropagation()
+ }
+ onSubmit?.(e)
+ }
+
return (
diff --git a/web/app/components/base/form/components/field/select.tsx b/web/app/components/base/form/components/field/select.tsx
index dee047e2eb..8a36a49510 100644
--- a/web/app/components/base/form/components/field/select.tsx
+++ b/web/app/components/base/form/components/field/select.tsx
@@ -11,7 +11,9 @@ type SelectFieldProps = {
options: Option[]
onChange?: (value: string) => void
className?: string
-} & Omit
+} & Omit & {
+ multiple?: false
+}
const SelectField = ({
label,
diff --git a/web/app/components/base/form/components/field/variable-or-constant-input.tsx b/web/app/components/base/form/components/field/variable-or-constant-input.tsx
index a07e356fa2..b8a96c5401 100644
--- a/web/app/components/base/form/components/field/variable-or-constant-input.tsx
+++ b/web/app/components/base/form/components/field/variable-or-constant-input.tsx
@@ -1,5 +1,5 @@
import type { ChangeEvent } from 'react'
-import { useState } from 'react'
+import { useCallback, useState } from 'react'
import { RiEditLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import SegmentedControl from '@/app/components/base/segmented-control'
@@ -33,9 +33,9 @@ const VariableOrConstantInputField = ({
},
]
- const handleVariableOrConstantChange = (value: string) => {
+ const handleVariableOrConstantChange = useCallback((value: string) => {
setVariableType(value)
- }
+ }, [setVariableType])
const handleVariableValueChange = () => {
console.log('Variable value changed')
diff --git a/web/app/components/base/form/hooks/use-get-form-values.ts b/web/app/components/base/form/hooks/use-get-form-values.ts
index 36100a724a..b7d08cc005 100644
--- a/web/app/components/base/form/hooks/use-get-form-values.ts
+++ b/web/app/components/base/form/hooks/use-get-form-values.ts
@@ -12,7 +12,7 @@ export const useGetFormValues = (form: AnyFormApi, formSchemas: FormSchema[]) =>
const getFormValues = useCallback((
{
- needCheckValidatedValues,
+ needCheckValidatedValues = true,
needTransformWhenSecretFieldIsPristine,
}: GetValuesOptions,
) => {
@@ -20,7 +20,7 @@ export const useGetFormValues = (form: AnyFormApi, formSchemas: FormSchema[]) =>
if (!needCheckValidatedValues) {
return {
values,
- isCheckValidated: false,
+ isCheckValidated: true,
}
}
diff --git a/web/app/components/base/form/index.stories.tsx b/web/app/components/base/form/index.stories.tsx
index c1b9e894e0..f170cb4771 100644
--- a/web/app/components/base/form/index.stories.tsx
+++ b/web/app/components/base/form/index.stories.tsx
@@ -102,14 +102,14 @@ const FormPlayground = () => {
options={{
...demoFormOpts,
validators: {
- onSubmit: ({ value }) => {
- const result = UserSchema.safeParse(value as typeof demoFormOpts.defaultValues)
+ onSubmit: ({ value: formValue }) => {
+ const result = UserSchema.safeParse(formValue as typeof demoFormOpts.defaultValues)
if (!result.success)
return result.error.issues[0].message
return undefined
},
},
- onSubmit: ({ value }) => {
+ onSubmit: () => {
setStatus('Successfully saved profile.')
},
}}
diff --git a/web/app/components/base/form/types.ts b/web/app/components/base/form/types.ts
index ce3b5ec965..268f9db89a 100644
--- a/web/app/components/base/form/types.ts
+++ b/web/app/components/base/form/types.ts
@@ -6,6 +6,7 @@ import type {
AnyFormApi,
FieldValidators,
} from '@tanstack/react-form'
+import type { Locale } from '@/i18n-config'
export type TypeWithI18N = {
en_US: T
@@ -36,7 +37,7 @@ export enum FormTypeEnum {
}
export type FormOption = {
- label: TypeWithI18N | string
+ label: string | TypeWithI18N | Record
value: string
show_on?: FormShowOnObject[]
icon?: string
@@ -44,23 +45,41 @@ export type FormOption = {
export type AnyValidators = FieldValidators
+export enum FormItemValidateStatusEnum {
+ Success = 'success',
+ Warning = 'warning',
+ Error = 'error',
+ Validating = 'validating',
+}
+
export type FormSchema = {
type: FormTypeEnum
name: string
- label: string | ReactNode | TypeWithI18N
+ label: string | ReactNode | TypeWithI18N | Record
required: boolean
+ multiple?: boolean
default?: any
- tooltip?: string | TypeWithI18N
+ description?: string | TypeWithI18N | Record
+ tooltip?: string | TypeWithI18N | Record
show_on?: FormShowOnObject[]
url?: string
scope?: string
- help?: string | TypeWithI18N
- placeholder?: string | TypeWithI18N
+ help?: string | TypeWithI18N | Record
+ placeholder?: string | TypeWithI18N | Record
options?: FormOption[]
labelClassName?: string
+ fieldClassName?: string
validators?: AnyValidators
showRadioUI?: boolean
disabled?: boolean
+ showCopy?: boolean
+ dynamicSelectParams?: {
+ plugin_id: string
+ provider: string
+ action: string
+ parameter: string
+ credential_id: string
+ }
}
export type FormValues = Record
@@ -69,11 +88,25 @@ export type GetValuesOptions = {
needTransformWhenSecretFieldIsPristine?: boolean
needCheckValidatedValues?: boolean
}
+
+export type FieldState = {
+ validateStatus?: FormItemValidateStatusEnum
+ help?: string | ReactNode
+ errors?: string[]
+ warnings?: string[]
+}
+
+export type SetFieldsParam = {
+ name: string
+ value?: any
+} & FieldState
+
export type FormRefObject = {
getForm: () => AnyFormApi
getFormValues: (obj: GetValuesOptions) => {
values: Record
isCheckValidated: boolean
}
+ setFields: (fields: SetFieldsParam[]) => void
}
export type FormRef = ForwardedRef
diff --git a/web/app/components/base/icons/assets/vender/line/alertsAndFeedback/warning.svg b/web/app/components/base/icons/assets/vender/line/alertsAndFeedback/warning.svg
new file mode 100644
index 0000000000..8174878acb
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/line/alertsAndFeedback/warning.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/line/arrows/IconR.svg b/web/app/components/base/icons/assets/vender/line/arrows/IconR.svg
new file mode 100644
index 0000000000..7ff1df98e2
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/line/arrows/IconR.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/line/others/apps-02.svg b/web/app/components/base/icons/assets/vender/line/others/apps-02.svg
deleted file mode 100644
index 8e1fec9ecc..0000000000
--- a/web/app/components/base/icons/assets/vender/line/others/apps-02.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/web/app/components/base/icons/assets/vender/line/others/exchange-02.svg b/web/app/components/base/icons/assets/vender/line/others/exchange-02.svg
deleted file mode 100644
index 45d2770277..0000000000
--- a/web/app/components/base/icons/assets/vender/line/others/exchange-02.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/web/app/components/base/icons/assets/vender/line/others/file-code.svg b/web/app/components/base/icons/assets/vender/line/others/file-code.svg
deleted file mode 100644
index eb77033a0a..0000000000
--- a/web/app/components/base/icons/assets/vender/line/others/file-code.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/web/app/components/base/icons/assets/vender/other/hourglass-shape.svg b/web/app/components/base/icons/assets/vender/other/hourglass-shape.svg
new file mode 100644
index 0000000000..150630f460
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/other/hourglass-shape.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/plugin/trigger.svg b/web/app/components/base/icons/assets/vender/plugin/trigger.svg
new file mode 100644
index 0000000000..261fcd02b7
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/plugin/trigger.svg
@@ -0,0 +1,8 @@
+
diff --git a/web/app/components/base/icons/assets/vender/solid/arrows/arrow-down-double-line.svg b/web/app/components/base/icons/assets/vender/solid/arrows/arrow-down-double-line.svg
new file mode 100644
index 0000000000..56caa01c59
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/solid/arrows/arrow-down-double-line.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/solid/arrows/arrow-down-round-fill.svg b/web/app/components/base/icons/assets/vender/solid/arrows/arrow-down-round-fill.svg
new file mode 100644
index 0000000000..48e70bcb51
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/solid/arrows/arrow-down-round-fill.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/solid/arrows/arrow-up-double-line.svg b/web/app/components/base/icons/assets/vender/solid/arrows/arrow-up-double-line.svg
new file mode 100644
index 0000000000..1f0b9858e1
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/solid/arrows/arrow-up-double-line.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/workflow/api-aggregate.svg b/web/app/components/base/icons/assets/vender/workflow/api-aggregate.svg
new file mode 100644
index 0000000000..aaf2206d21
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/workflow/api-aggregate.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/workflow/asterisk.svg b/web/app/components/base/icons/assets/vender/workflow/asterisk.svg
new file mode 100644
index 0000000000..d273c7e3d5
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/workflow/asterisk.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/workflow/calendar-check-line.svg b/web/app/components/base/icons/assets/vender/workflow/calendar-check-line.svg
new file mode 100644
index 0000000000..2c7f148c71
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/workflow/calendar-check-line.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/assets/vender/workflow/schedule.svg b/web/app/components/base/icons/assets/vender/workflow/schedule.svg
new file mode 100644
index 0000000000..69977c4c7f
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/workflow/schedule.svg
@@ -0,0 +1,5 @@
+
diff --git a/web/app/components/base/icons/assets/vender/workflow/trigger-all.svg b/web/app/components/base/icons/assets/vender/workflow/trigger-all.svg
new file mode 100644
index 0000000000..dedcc0ad3c
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/workflow/trigger-all.svg
@@ -0,0 +1,8 @@
+
diff --git a/web/app/components/base/icons/assets/vender/workflow/webhook-line.svg b/web/app/components/base/icons/assets/vender/workflow/webhook-line.svg
new file mode 100644
index 0000000000..16fd30a961
--- /dev/null
+++ b/web/app/components/base/icons/assets/vender/workflow/webhook-line.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/app/components/base/icons/icon-gallery.stories.tsx b/web/app/components/base/icons/icon-gallery.stories.tsx
new file mode 100644
index 0000000000..7da71b3b0b
--- /dev/null
+++ b/web/app/components/base/icons/icon-gallery.stories.tsx
@@ -0,0 +1,258 @@
+import type { Meta, StoryObj } from '@storybook/nextjs'
+import React from 'react'
+
+declare const require: any
+
+type IconComponent = React.ComponentType>
+
+type IconEntry = {
+ name: string
+ category: string
+ path: string
+ Component: IconComponent
+}
+
+const iconContext = require.context('./src', true, /\.tsx$/)
+
+const iconEntries: IconEntry[] = iconContext
+ .keys()
+ .filter((key: string) => !key.endsWith('.stories.tsx') && !key.endsWith('.spec.tsx'))
+ .map((key: string) => {
+ const mod = iconContext(key)
+ const Component = mod.default as IconComponent | undefined
+ if (!Component)
+ return null
+
+ const relativePath = key.replace(/^\.\//, '')
+ const path = `app/components/base/icons/src/${relativePath}`
+ const parts = relativePath.split('/')
+ const fileName = parts.pop() || ''
+ const category = parts.length ? parts.join('/') : '(root)'
+ const name = Component.displayName || fileName.replace(/\.tsx$/, '')
+
+ return {
+ name,
+ category,
+ path,
+ Component,
+ }
+ })
+ .filter(Boolean) as IconEntry[]
+
+const sortedEntries = [...iconEntries].sort((a, b) => {
+ if (a.category === b.category)
+ return a.name.localeCompare(b.name)
+ return a.category.localeCompare(b.category)
+})
+
+const filterEntries = (entries: IconEntry[], query: string) => {
+ const normalized = query.trim().toLowerCase()
+ if (!normalized)
+ return entries
+
+ return entries.filter(entry =>
+ entry.name.toLowerCase().includes(normalized)
+ || entry.path.toLowerCase().includes(normalized)
+ || entry.category.toLowerCase().includes(normalized),
+ )
+}
+
+const groupByCategory = (entries: IconEntry[]) => entries.reduce((acc, entry) => {
+ if (!acc[entry.category])
+ acc[entry.category] = []
+
+ acc[entry.category].push(entry)
+ return acc
+}, {} as Record)
+
+const containerStyle: React.CSSProperties = {
+ padding: 24,
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 24,
+}
+
+const headerStyle: React.CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 8,
+}
+
+const controlsStyle: React.CSSProperties = {
+ display: 'flex',
+ alignItems: 'center',
+ gap: 12,
+ flexWrap: 'wrap',
+}
+
+const searchInputStyle: React.CSSProperties = {
+ padding: '8px 12px',
+ minWidth: 280,
+ borderRadius: 6,
+ border: '1px solid #d0d0d5',
+}
+
+const toggleButtonStyle: React.CSSProperties = {
+ padding: '8px 12px',
+ borderRadius: 6,
+ border: '1px solid #d0d0d5',
+ background: '#fff',
+ cursor: 'pointer',
+}
+
+const emptyTextStyle: React.CSSProperties = { color: '#5f5f66' }
+
+const sectionStyle: React.CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 12,
+}
+
+const gridStyle: React.CSSProperties = {
+ display: 'grid',
+ gap: 12,
+ gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))',
+}
+
+const cardStyle: React.CSSProperties = {
+ border: '1px solid #e1e1e8',
+ borderRadius: 8,
+ padding: 12,
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 8,
+ minHeight: 140,
+}
+
+const previewBaseStyle: React.CSSProperties = {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ minHeight: 48,
+ borderRadius: 6,
+}
+
+const nameButtonBaseStyle: React.CSSProperties = {
+ display: 'inline-flex',
+ padding: 0,
+ border: 'none',
+ background: 'transparent',
+ font: 'inherit',
+ cursor: 'pointer',
+ textAlign: 'left',
+ fontWeight: 600,
+}
+
+const PREVIEW_SIZE = 40
+
+const IconGalleryStory = () => {
+ const [query, setQuery] = React.useState('')
+ const [copiedPath, setCopiedPath] = React.useState(null)
+ const [previewTheme, setPreviewTheme] = React.useState<'light' | 'dark'>('light')
+
+ const filtered = React.useMemo(() => filterEntries(sortedEntries, query), [query])
+
+ const grouped = React.useMemo(() => groupByCategory(filtered), [filtered])
+
+ const categoryOrder = React.useMemo(
+ () => Object.keys(grouped).sort((a, b) => a.localeCompare(b)),
+ [grouped],
+ )
+
+ React.useEffect(() => {
+ if (!copiedPath)
+ return undefined
+
+ const timerId = window.setTimeout(() => {
+ setCopiedPath(null)
+ }, 1200)
+
+ return () => window.clearTimeout(timerId)
+ }, [copiedPath])
+
+ const handleCopy = React.useCallback((text: string) => {
+ navigator.clipboard?.writeText(text)
+ .then(() => {
+ setCopiedPath(text)
+ })
+ .catch((err) => {
+ console.error('Failed to copy icon path:', err)
+ })
+ }, [])
+
+ return (
+
+
+ {categoryOrder.length === 0 && (
+
No icons match the current filter.
+ )}
+ {categoryOrder.map(category => (
+
+ {category}
+
+ {grouped[category].map(entry => (
+
+
+
+
+
+
+ ))}
+
+
+ ))}
+
+ )
+}
+
+const meta: Meta = {
+ title: 'Base/Icons/Icon Gallery',
+ component: IconGalleryStory,
+ parameters: {
+ layout: 'fullscreen',
+ },
+}
+
+export default meta
+
+type Story = StoryObj
+
+export const All: Story = {
+ render: () => ,
+}
diff --git a/web/app/components/base/icons/src/image/llm/BaichuanTextCn.module.css b/web/app/components/base/icons/src/image/llm/BaichuanTextCn.module.css
new file mode 100644
index 0000000000..97ab9b22f9
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/BaichuanTextCn.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/baichuan-text-cn.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/BaichuanTextCn.tsx b/web/app/components/base/icons/src/image/llm/BaichuanTextCn.tsx
new file mode 100644
index 0000000000..be9a407eb2
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/BaichuanTextCn.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './BaichuanTextCn.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'BaichuanTextCn'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/Minimax.module.css b/web/app/components/base/icons/src/image/llm/Minimax.module.css
new file mode 100644
index 0000000000..551ecc3c62
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/Minimax.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/minimax.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/Minimax.tsx b/web/app/components/base/icons/src/image/llm/Minimax.tsx
new file mode 100644
index 0000000000..7df7e3fcbc
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/Minimax.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './Minimax.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'Minimax'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/MinimaxText.module.css b/web/app/components/base/icons/src/image/llm/MinimaxText.module.css
new file mode 100644
index 0000000000..a63be49e8b
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/MinimaxText.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/minimax-text.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/MinimaxText.tsx b/web/app/components/base/icons/src/image/llm/MinimaxText.tsx
new file mode 100644
index 0000000000..840e8cb439
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/MinimaxText.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './MinimaxText.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'MinimaxText'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/Tongyi.module.css b/web/app/components/base/icons/src/image/llm/Tongyi.module.css
new file mode 100644
index 0000000000..3ca440768c
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/Tongyi.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/tongyi.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/Tongyi.tsx b/web/app/components/base/icons/src/image/llm/Tongyi.tsx
new file mode 100644
index 0000000000..2f62f1a355
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/Tongyi.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './Tongyi.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'Tongyi'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/TongyiText.module.css b/web/app/components/base/icons/src/image/llm/TongyiText.module.css
new file mode 100644
index 0000000000..f713671808
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/TongyiText.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/tongyi-text.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/TongyiText.tsx b/web/app/components/base/icons/src/image/llm/TongyiText.tsx
new file mode 100644
index 0000000000..a52f63c248
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/TongyiText.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './TongyiText.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'TongyiText'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/TongyiTextCn.module.css b/web/app/components/base/icons/src/image/llm/TongyiTextCn.module.css
new file mode 100644
index 0000000000..d07e6e8bc4
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/TongyiTextCn.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/tongyi-text-cn.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/TongyiTextCn.tsx b/web/app/components/base/icons/src/image/llm/TongyiTextCn.tsx
new file mode 100644
index 0000000000..c982c73aed
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/TongyiTextCn.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './TongyiTextCn.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'TongyiTextCn'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/Wxyy.module.css b/web/app/components/base/icons/src/image/llm/Wxyy.module.css
new file mode 100644
index 0000000000..44344a495f
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/Wxyy.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/wxyy.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/Wxyy.tsx b/web/app/components/base/icons/src/image/llm/Wxyy.tsx
new file mode 100644
index 0000000000..a3c494811e
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/Wxyy.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './Wxyy.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'Wxyy'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/WxyyText.module.css b/web/app/components/base/icons/src/image/llm/WxyyText.module.css
new file mode 100644
index 0000000000..58a0c62047
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/WxyyText.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/wxyy-text.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/WxyyText.tsx b/web/app/components/base/icons/src/image/llm/WxyyText.tsx
new file mode 100644
index 0000000000..e5dd6e8803
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/WxyyText.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './WxyyText.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'WxyyText'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/WxyyTextCn.module.css b/web/app/components/base/icons/src/image/llm/WxyyTextCn.module.css
new file mode 100644
index 0000000000..fb5839ab07
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/WxyyTextCn.module.css
@@ -0,0 +1,5 @@
+.wrapper {
+ display: inline-flex;
+ background: url(~@/app/components/base/icons/assets/image/llm/wxyy-text-cn.png) center center no-repeat;
+ background-size: contain;
+}
diff --git a/web/app/components/base/icons/src/image/llm/WxyyTextCn.tsx b/web/app/components/base/icons/src/image/llm/WxyyTextCn.tsx
new file mode 100644
index 0000000000..32108adab4
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/WxyyTextCn.tsx
@@ -0,0 +1,20 @@
+// GENERATE BY script
+// DON NOT EDIT IT MANUALLY
+
+import * as React from 'react'
+import cn from '@/utils/classnames'
+import s from './WxyyTextCn.module.css'
+
+const Icon = (
+ {
+ ref,
+ className,
+ ...restProps
+ }: React.DetailedHTMLProps, HTMLSpanElement> & {
+ ref?: React.RefObject;
+ },
+) =>
+
+Icon.displayName = 'WxyyTextCn'
+
+export default Icon
diff --git a/web/app/components/base/icons/src/image/llm/index.ts b/web/app/components/base/icons/src/image/llm/index.ts
new file mode 100644
index 0000000000..3a4e64ac18
--- /dev/null
+++ b/web/app/components/base/icons/src/image/llm/index.ts
@@ -0,0 +1,9 @@
+export { default as BaichuanTextCn } from './BaichuanTextCn'
+export { default as MinimaxText } from './MinimaxText'
+export { default as Minimax } from './Minimax'
+export { default as TongyiTextCn } from './TongyiTextCn'
+export { default as TongyiText } from './TongyiText'
+export { default as Tongyi } from './Tongyi'
+export { default as WxyyTextCn } from './WxyyTextCn'
+export { default as WxyyText } from './WxyyText'
+export { default as Wxyy } from './Wxyy'
diff --git a/web/app/components/base/icons/src/public/billing/AwsMarketplaceDark.tsx b/web/app/components/base/icons/src/public/billing/AwsMarketplaceDark.tsx
index 5aa2d6c430..7096a4d2eb 100644
--- a/web/app/components/base/icons/src/public/billing/AwsMarketplaceDark.tsx
+++ b/web/app/components/base/icons/src/public/billing/AwsMarketplaceDark.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlue.tsx b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlue.tsx
index 85697f9dae..8d3e6a8a8a 100644
--- a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlue.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlue.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlueLight.tsx b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlueLight.tsx
index bf4264f1bd..f44856be61 100644
--- a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlueLight.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectBlueLight.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectOrange.tsx b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectOrange.tsx
index bd6cda4470..fe76f5917f 100644
--- a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectOrange.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectOrange.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectPurple.tsx b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectPurple.tsx
index b70808ef8c..f5c5e7ba3a 100644
--- a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectPurple.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectPurple.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectTeal.tsx b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectTeal.tsx
index ddd04a1911..0d2a07e405 100644
--- a/web/app/components/base/icons/src/public/knowledge/OptionCardEffectTeal.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/OptionCardEffectTeal.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/dataset-card/ExternalKnowledgeBase.tsx b/web/app/components/base/icons/src/public/knowledge/dataset-card/ExternalKnowledgeBase.tsx
index ea6ce30704..06bb8086bc 100644
--- a/web/app/components/base/icons/src/public/knowledge/dataset-card/ExternalKnowledgeBase.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/dataset-card/ExternalKnowledgeBase.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/dataset-card/General.tsx b/web/app/components/base/icons/src/public/knowledge/dataset-card/General.tsx
index 6508ed57c6..6665039002 100644
--- a/web/app/components/base/icons/src/public/knowledge/dataset-card/General.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/dataset-card/General.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/dataset-card/Graph.tsx b/web/app/components/base/icons/src/public/knowledge/dataset-card/Graph.tsx
index c1360c52ca..127367f873 100644
--- a/web/app/components/base/icons/src/public/knowledge/dataset-card/Graph.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/dataset-card/Graph.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/dataset-card/ParentChild.tsx b/web/app/components/base/icons/src/public/knowledge/dataset-card/ParentChild.tsx
index 7c6c3baa7b..922cb2c825 100644
--- a/web/app/components/base/icons/src/public/knowledge/dataset-card/ParentChild.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/dataset-card/ParentChild.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/dataset-card/Qa.tsx b/web/app/components/base/icons/src/public/knowledge/dataset-card/Qa.tsx
index 34ef88141e..ac41a8b153 100644
--- a/web/app/components/base/icons/src/public/knowledge/dataset-card/Qa.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/dataset-card/Qa.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject>;
},
) =>
diff --git a/web/app/components/base/icons/src/public/knowledge/online-drive/BucketsBlue.tsx b/web/app/components/base/icons/src/public/knowledge/online-drive/BucketsBlue.tsx
index 9fd923458e..cfd9570081 100644
--- a/web/app/components/base/icons/src/public/knowledge/online-drive/BucketsBlue.tsx
+++ b/web/app/components/base/icons/src/public/knowledge/online-drive/BucketsBlue.tsx
@@ -11,7 +11,7 @@ const Icon = (
ref,
...props
}: React.SVGProps & {
- ref?: React.RefObject>;
+ ref?: React.RefObject