mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 01:48:04 +08:00
feat: add isPreviewable guard for binary file preview in artifacts
Add a unified isPreviewable flag to useFileTypeInfo that guards against rendering binary files as text in both skill artifacts and variable inspect artifacts preview. Upgrade extension arrays to Sets for O(1) lookups.
This commit is contained in:
@ -56,7 +56,7 @@ const FileContentPanel = () => {
|
||||
|
||||
const currentFileNode = fileTabId ? nodeMap?.get(fileTabId) : undefined
|
||||
|
||||
const { isMarkdown, isCodeOrText, isImage, isVideo, isSQLite, isEditable } = useFileTypeInfo(currentFileNode)
|
||||
const { isMarkdown, isCodeOrText, isImage, isVideo, isSQLite, isEditable, isPreviewable } = useFileTypeInfo(currentFileNode)
|
||||
|
||||
const { fileContent, downloadUrlData, isLoading, error } = useSkillFileData(appId, fileTabId, isEditable)
|
||||
|
||||
@ -210,7 +210,7 @@ const FileContentPanel = () => {
|
||||
const downloadUrl = downloadUrlData?.download_url || ''
|
||||
const fileName = currentFileNode?.name || ''
|
||||
const fileSize = currentFileNode?.size
|
||||
const isUnsupportedFile = !isMarkdown && !isCodeOrText && !isImage && !isVideo && !isSQLite
|
||||
const isUnsupportedFile = !isPreviewable
|
||||
|
||||
return (
|
||||
<div className="h-full w-full overflow-auto bg-components-panel-bg">
|
||||
|
||||
@ -16,11 +16,25 @@ export type FileTypeInfo = {
|
||||
isSQLite: boolean
|
||||
isEditable: boolean
|
||||
isMediaFile: boolean
|
||||
isPreviewable: boolean
|
||||
}
|
||||
|
||||
export function useFileTypeInfo(fileNode: { name: string, extension?: string | null } | undefined): FileTypeInfo {
|
||||
return useMemo(() => {
|
||||
const ext = getFileExtension(fileNode?.name, fileNode?.extension ?? undefined)
|
||||
if (!fileNode) {
|
||||
return {
|
||||
isMarkdown: false,
|
||||
isCodeOrText: false,
|
||||
isImage: false,
|
||||
isVideo: false,
|
||||
isSQLite: false,
|
||||
isEditable: false,
|
||||
isMediaFile: false,
|
||||
isPreviewable: false,
|
||||
}
|
||||
}
|
||||
|
||||
const ext = getFileExtension(fileNode.name, fileNode.extension ?? undefined)
|
||||
const markdown = isMarkdownFile(ext)
|
||||
const image = isImageFile(ext)
|
||||
const video = isVideoFile(ext)
|
||||
@ -36,6 +50,7 @@ export function useFileTypeInfo(fileNode: { name: string, extension?: string | n
|
||||
isSQLite: sqlite,
|
||||
isEditable: editable,
|
||||
isMediaFile: image || video,
|
||||
isPreviewable: editable || image || video || sqlite,
|
||||
}
|
||||
}, [fileNode?.name, fileNode?.extension])
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types'
|
||||
|
||||
const MARKDOWN_EXTENSIONS = ['md', 'markdown', 'mdx']
|
||||
const CODE_EXTENSIONS = ['json', 'yaml', 'yml', 'toml', 'js', 'jsx', 'ts', 'tsx', 'py', 'schema']
|
||||
const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp', 'ico', 'tiff', 'psd', 'heic', 'heif', 'avif']
|
||||
const VIDEO_EXTENSIONS = ['mp4', 'mov', 'webm', 'mpeg', 'mpg', 'm4v', 'avi', 'mkv', 'flv', 'wmv', '3gp']
|
||||
const SQLITE_EXTENSIONS = ['db', 'sqlite', 'sqlite3']
|
||||
const MARKDOWN_EXTENSIONS = new Set(['md', 'markdown', 'mdx'])
|
||||
const CODE_EXTENSIONS = new Set(['json', 'yaml', 'yml', 'toml', 'js', 'jsx', 'ts', 'tsx', 'py', 'schema'])
|
||||
const IMAGE_EXTENSIONS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp', 'ico', 'tiff', 'psd', 'heic', 'heif', 'avif'])
|
||||
const VIDEO_EXTENSIONS = new Set(['mp4', 'mov', 'webm', 'mpeg', 'mpg', 'm4v', 'avi', 'mkv', 'flv', 'wmv', '3gp'])
|
||||
const SQLITE_EXTENSIONS = new Set(['db', 'sqlite', 'sqlite3'])
|
||||
|
||||
const BINARY_EXTENSIONS = [
|
||||
const BINARY_EXTENSIONS = new Set([
|
||||
'mp3',
|
||||
'wav',
|
||||
'ogg',
|
||||
@ -76,7 +76,7 @@ const BINARY_EXTENSIONS = [
|
||||
'ipa',
|
||||
'aab',
|
||||
'lock',
|
||||
]
|
||||
])
|
||||
|
||||
export function getFileExtension(name?: string, extension?: string): string {
|
||||
if (extension)
|
||||
@ -105,8 +105,8 @@ const EXTENSION_TO_ICON_TYPE = new Map<string, FileAppearanceTypeEnum>(
|
||||
[PPT_EXTENSIONS, FileAppearanceTypeEnum.ppt],
|
||||
[CODE_EXTENSIONS, FileAppearanceTypeEnum.code],
|
||||
[SQLITE_EXTENSIONS, FileAppearanceTypeEnum.database],
|
||||
] as [string[], FileAppearanceTypeEnum][]).flatMap(
|
||||
([exts, type]) => exts.map(e => [e, type] as [string, FileAppearanceTypeEnum]),
|
||||
] as [Iterable<string>, FileAppearanceTypeEnum][]).flatMap(
|
||||
([exts, type]) => [...exts].map(e => [e, type] as [string, FileAppearanceTypeEnum]),
|
||||
),
|
||||
)
|
||||
|
||||
@ -116,11 +116,11 @@ export function getFileIconType(name: string, ext?: string | null): FileAppearan
|
||||
}
|
||||
|
||||
export function isMarkdownFile(extension: string): boolean {
|
||||
return MARKDOWN_EXTENSIONS.includes(extension)
|
||||
return MARKDOWN_EXTENSIONS.has(extension)
|
||||
}
|
||||
|
||||
export function isBinaryFile(extension: string): boolean {
|
||||
return BINARY_EXTENSIONS.includes(extension)
|
||||
return BINARY_EXTENSIONS.has(extension)
|
||||
}
|
||||
|
||||
export function isTextLikeFile(extension: string): boolean {
|
||||
@ -128,15 +128,15 @@ export function isTextLikeFile(extension: string): boolean {
|
||||
}
|
||||
|
||||
export function isImageFile(extension: string): boolean {
|
||||
return IMAGE_EXTENSIONS.includes(extension)
|
||||
return IMAGE_EXTENSIONS.has(extension)
|
||||
}
|
||||
|
||||
export function isVideoFile(extension: string): boolean {
|
||||
return VIDEO_EXTENSIONS.includes(extension)
|
||||
return VIDEO_EXTENSIONS.has(extension)
|
||||
}
|
||||
|
||||
export function isSQLiteFile(extension: string): boolean {
|
||||
return SQLITE_EXTENSIONS.includes(extension)
|
||||
return SQLITE_EXTENSIONS.has(extension)
|
||||
}
|
||||
|
||||
export function getFileLanguage(name: string): string {
|
||||
|
||||
@ -41,12 +41,22 @@ const ReadOnlyFilePreview = ({
|
||||
() => ({ name: fileName, extension }),
|
||||
[fileName, extension],
|
||||
)
|
||||
const { isMarkdown, isCodeOrText, isImage, isVideo, isSQLite } = useFileTypeInfo(fileNode)
|
||||
const isTextFile = isMarkdown || isCodeOrText
|
||||
const { isMarkdown, isCodeOrText, isImage, isVideo, isSQLite, isPreviewable } = useFileTypeInfo(fileNode)
|
||||
const isTextFile = isPreviewable && (isMarkdown || isCodeOrText)
|
||||
const { data: textContent, isLoading: isTextLoading } = useFetchTextContent(
|
||||
isTextFile ? downloadUrl : undefined,
|
||||
)
|
||||
|
||||
if (!isPreviewable) {
|
||||
return (
|
||||
<UnsupportedFileDownload
|
||||
name={fileName}
|
||||
size={fileSize ?? undefined}
|
||||
downloadUrl={downloadUrl}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (isTextFile && isTextLoading)
|
||||
return <Loading type="area" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user