feat: multimodal support (image) (#27793)

Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Wu Tianwei
2025-12-09 11:44:50 +08:00
committed by GitHub
parent a44b800c85
commit 14d1b3f9b3
77 changed files with 2932 additions and 579 deletions

View File

@@ -0,0 +1,23 @@
import React from 'react'
type ImageRenderProps = {
sourceUrl: string
name: string
}
const ImageRender = ({
sourceUrl,
name,
}: ImageRenderProps) => {
return (
<div className='size-full border-[2px] border-effects-image-frame shadow-xs'>
<img
className='size-full object-cover'
src={sourceUrl}
alt={name}
/>
</div>
)
}
export default React.memo(ImageRender)

View File

@@ -0,0 +1,87 @@
import React, { useCallback } from 'react'
import ImageRender from './image-render'
import type { VariantProps } from 'class-variance-authority'
import { cva } from 'class-variance-authority'
import cn from '@/utils/classnames'
import { getFileAppearanceType } from '../file-uploader/utils'
import { FileTypeIcon } from '../file-uploader'
import Tooltip from '../tooltip'
const FileThumbVariants = cva(
'flex items-center justify-center cursor-pointer',
{
variants: {
size: {
sm: 'size-6',
md: 'size-8',
},
},
defaultVariants: {
size: 'sm',
},
},
)
export type FileEntity = {
name: string
size: number
extension: string
mimeType: string
sourceUrl: string
}
type FileThumbProps = {
file: FileEntity
className?: string
onClick?: (file: FileEntity) => void
} & VariantProps<typeof FileThumbVariants>
const FileThumb = ({
file,
size,
className,
onClick,
}: FileThumbProps) => {
const { name, mimeType, sourceUrl } = file
const isImage = mimeType.startsWith('image/')
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.preventDefault()
onClick?.(file)
}, [onClick, file])
return (
<Tooltip
popupContent={name}
popupClassName='p-1.5 rounded-lg system-xs-medium text-text-secondary'
position='top'
>
<div
className={cn(
FileThumbVariants({ size, className }),
isImage
? 'p-px'
: 'rounded-md border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs hover:bg-components-panel-on-panel-item-bg-alt',
)}
onClick={handleClick}
>
{
isImage ? (
<ImageRender
sourceUrl={sourceUrl}
name={name}
/>
) : (
<FileTypeIcon
type={getFileAppearanceType(name, mimeType)}
size='sm'
/>
)
}
</div>
</Tooltip>
)
}
export default React.memo(FileThumb)

View File

@@ -26,10 +26,21 @@ export const getFileUploadErrorMessage = (error: any, defaultMessage: string, t:
return defaultMessage
}
type FileUploadResponse = {
created_at: number
created_by: string
extension: string
id: string
mime_type: string
name: string
preview_url: string | null
size: number
source_url: string
}
type FileUploadParams = {
file: File
onProgressCallback: (progress: number) => void
onSuccessCallback: (res: { id: string }) => void
onSuccessCallback: (res: FileUploadResponse) => void
onErrorCallback: (error?: any) => void
}
type FileUpload = (v: FileUploadParams, isPublic?: boolean, url?: string) => void
@@ -53,8 +64,8 @@ export const fileUpload: FileUpload = ({
data: formData,
onprogress: onProgress,
}, isPublic, url)
.then((res: { id: string }) => {
onSuccessCallback(res)
.then((res) => {
onSuccessCallback(res as FileUploadResponse)
})
.catch((error) => {
onErrorCallback(error)
@@ -174,9 +185,9 @@ export const getProcessedFilesFromResponse = (files: FileResponse[]) => {
const detectedTypeFromMime = getSupportFileType('', fileItem.mime_type)
if (detectedTypeFromFileName
&& detectedTypeFromMime
&& detectedTypeFromFileName === detectedTypeFromMime
&& detectedTypeFromFileName !== fileItem.type)
&& detectedTypeFromMime
&& detectedTypeFromFileName === detectedTypeFromMime
&& detectedTypeFromFileName !== fileItem.type)
supportFileType = detectedTypeFromFileName
}