From b72e73795b172f40e5c5a2a009b837375bc89926 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 2 May 2026 22:01:47 -0700 Subject: [PATCH 1/3] feat(files): allow image uploads in workspace files --- .../workspace/[workspaceId]/files/files.tsx | 3 ++ apps/sim/lib/uploads/utils/validation.ts | 29 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/files/files.tsx b/apps/sim/app/workspace/[workspaceId]/files/files.tsx index b172d8bc5d9..584c53453f9 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/files.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/files.tsx @@ -43,6 +43,7 @@ import { SUPPORTED_AUDIO_EXTENSIONS, SUPPORTED_CODE_EXTENSIONS, SUPPORTED_DOCUMENT_EXTENSIONS, + SUPPORTED_IMAGE_EXTENSIONS, SUPPORTED_VIDEO_EXTENSIONS, } from '@/lib/uploads/utils/validation' import type { @@ -89,6 +90,7 @@ const SUPPORTED_EXTENSIONS = [ ...SUPPORTED_CODE_EXTENSIONS, ...SUPPORTED_AUDIO_EXTENSIONS, ...SUPPORTED_VIDEO_EXTENSIONS, + ...SUPPORTED_IMAGE_EXTENSIONS, ] as const const ACCEPT_ATTR = SUPPORTED_EXTENSIONS.map((ext) => `.${ext}`).join(',') @@ -125,6 +127,7 @@ function formatFileType(mimeType: string | null, filename: string): string { if (mimeType?.startsWith('audio/')) return 'Audio' if (mimeType?.startsWith('video/')) return 'Video' + if (mimeType?.startsWith('image/')) return 'Image' const ext = getFileExtension(filename) if (ext) return ext.toUpperCase() diff --git a/apps/sim/lib/uploads/utils/validation.ts b/apps/sim/lib/uploads/utils/validation.ts index f4e45586793..af0a5581fba 100644 --- a/apps/sim/lib/uploads/utils/validation.ts +++ b/apps/sim/lib/uploads/utils/validation.ts @@ -95,13 +95,31 @@ export const SUPPORTED_AUDIO_EXTENSIONS = [ export const SUPPORTED_VIDEO_EXTENSIONS = ['mp4', 'mov', 'avi', 'mkv', 'webm'] as const +export const SUPPORTED_IMAGE_EXTENSIONS = [ + 'png', + 'jpg', + 'jpeg', + 'gif', + 'webp', + 'svg', + 'bmp', + 'tif', + 'tiff', + 'heic', + 'heif', + 'avif', + 'ico', +] as const + export type SupportedDocumentExtension = (typeof SUPPORTED_DOCUMENT_EXTENSIONS)[number] export type SupportedAudioExtension = (typeof SUPPORTED_AUDIO_EXTENSIONS)[number] export type SupportedVideoExtension = (typeof SUPPORTED_VIDEO_EXTENSIONS)[number] +export type SupportedImageExtension = (typeof SUPPORTED_IMAGE_EXTENSIONS)[number] export type SupportedMediaExtension = | SupportedDocumentExtension | SupportedAudioExtension | SupportedVideoExtension + | SupportedImageExtension export const SUPPORTED_MIME_TYPES: Record = { pdf: ['application/pdf', 'application/x-pdf'], @@ -180,14 +198,19 @@ const SUPPORTED_IMAGE_MIME_TYPES = [ 'image/gif', 'image/webp', 'image/svg+xml', + 'image/bmp', + 'image/tiff', + 'image/heic', + 'image/heif', + 'image/avif', + 'image/x-icon', + 'image/vnd.microsoft.icon', ] -const SUPPORTED_IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg'] - export const CHAT_ACCEPT_ATTRIBUTE = [ ACCEPT_ATTRIBUTE, ...SUPPORTED_IMAGE_MIME_TYPES, - ...SUPPORTED_IMAGE_EXTENSIONS, + ...SUPPORTED_IMAGE_EXTENSIONS.map((ext) => `.${ext}`), ].join(',') export interface FileValidationError { From 9689bcacee183ccf19d781d4f24784674e6e6200 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sun, 3 May 2026 02:30:51 -0700 Subject: [PATCH 2/3] fix(files): include images in workspace type filter --- .../app/workspace/[workspaceId]/files/files.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/files/files.tsx b/apps/sim/app/workspace/[workspaceId]/files/files.tsx index 584c53453f9..4ac26172e18 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/files.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/files.tsx @@ -36,6 +36,7 @@ import { getFileExtension, getMimeTypeFromExtension, isAudioFileType, + isImageFileType, isVideoFileType, } from '@/lib/uploads/utils/file-utils' import { @@ -249,6 +250,7 @@ export function Files() { if (typeFilter.includes('document') && isSupportedExtension(ext)) return true if (typeFilter.includes('audio') && isAudioFileType(f.type)) return true if (typeFilter.includes('video') && isVideoFileType(f.type)) return true + if (typeFilter.includes('image') && isImageFileType(f.type)) return true return false }) } @@ -929,9 +931,14 @@ export function Files() { typeFilter.length === 0 ? 'All' : typeFilter.length === 1 - ? (({ document: 'Documents', audio: 'Audio', video: 'Video' } as Record)[ - typeFilter[0] - ] ?? typeFilter[0]) + ? (( + { + document: 'Documents', + image: 'Images', + audio: 'Audio', + video: 'Video', + } as Record + )[typeFilter[0]] ?? typeFilter[0]) : `${typeFilter.length} selected` const sizeDisplayLabel = @@ -957,6 +964,7 @@ export function Files() { 0) { const typeLabels: Record = { document: 'Documents', + image: 'Images', audio: 'Audio', video: 'Video', } From 81f62ff14ab0361f43cde07e51a4f13bb0b620c9 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sun, 3 May 2026 02:43:16 -0700 Subject: [PATCH 3/3] fix(files): match all image mime types in image filter --- apps/sim/app/workspace/[workspaceId]/files/files.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/files/files.tsx b/apps/sim/app/workspace/[workspaceId]/files/files.tsx index 4ac26172e18..e259a7dad2a 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/files.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/files.tsx @@ -36,7 +36,6 @@ import { getFileExtension, getMimeTypeFromExtension, isAudioFileType, - isImageFileType, isVideoFileType, } from '@/lib/uploads/utils/file-utils' import { @@ -250,7 +249,7 @@ export function Files() { if (typeFilter.includes('document') && isSupportedExtension(ext)) return true if (typeFilter.includes('audio') && isAudioFileType(f.type)) return true if (typeFilter.includes('video') && isVideoFileType(f.type)) return true - if (typeFilter.includes('image') && isImageFileType(f.type)) return true + if (typeFilter.includes('image') && f.type?.startsWith('image/')) return true return false }) }