Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

import { useEffect, useMemo, useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useQueryClient } from '@tanstack/react-query'
import { X } from 'lucide-react'
import { useParams } from 'next/navigation'
import { Button, Combobox } from '@/components/emcn/components'
import { Progress } from '@/components/ui/progress'
import { cn } from '@/lib/core/utils/cn'
import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace'
import { getExtensionFromMimeType } from '@/lib/uploads/utils/file-utils'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useWorkspaceFiles, workspaceFilesKeys } from '@/hooks/queries/workspace-files'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'

Expand Down Expand Up @@ -150,8 +151,6 @@ export function FileUpload({
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlockId)
const [uploadingFiles, setUploadingFiles] = useState<UploadingFile[]>([])
const [uploadProgress, setUploadProgress] = useState(0)
const [workspaceFiles, setWorkspaceFiles] = useState<WorkspaceFileRecord[]>([])
const [loadingWorkspaceFiles, setLoadingWorkspaceFiles] = useState(false)
const [uploadError, setUploadError] = useState<string | null>(null)
const [inputValue, setInputValue] = useState('')

Expand All @@ -163,25 +162,14 @@ export function FileUpload({
const params = useParams()
const workspaceId = params?.workspaceId as string

const value = isPreview ? previewValue : storeValue

const loadWorkspaceFiles = async () => {
if (!workspaceId || isPreview) return

try {
setLoadingWorkspaceFiles(true)
const response = await fetch(`/api/workspaces/${workspaceId}/files`)
const data = await response.json()
const queryClient = useQueryClient()
const {
data: workspaceFiles = [],
isLoading: loadingWorkspaceFiles,
refetch: refetchWorkspaceFiles,
} = useWorkspaceFiles(workspaceId)
Comment thread
icecrasher321 marked this conversation as resolved.
Outdated

if (data.success) {
setWorkspaceFiles(data.files || [])
}
} catch (error) {
logger.error('Error loading workspace files:', error)
} finally {
setLoadingWorkspaceFiles(false)
}
}
const value = isPreview ? previewValue : storeValue

/**
* Checks if a file's MIME type matches the accepted types
Expand Down Expand Up @@ -226,10 +214,6 @@ export function FileUpload({
return !isAlreadySelected
})

useEffect(() => {
void loadWorkspaceFiles()
}, [workspaceId])

/**
* Opens file dialog
*/
Expand Down Expand Up @@ -394,7 +378,7 @@ export function FileUpload({
setUploadError(null)

if (workspaceId) {
void loadWorkspaceFiles()
void queryClient.invalidateQueries({ queryKey: workspaceFilesKeys.lists() })
}

if (uploadedFiles.length === 1) {
Expand Down Expand Up @@ -726,7 +710,7 @@ export function FileUpload({
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
if (open) void refetchWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : '+ Add More'}
disabled={disabled || loadingWorkspaceFiles}
Expand All @@ -746,7 +730,7 @@ export function FileUpload({
onInputChange={handleComboboxChange}
onClear={(e) => handleRemoveFile(filesArray[0], e)}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
if (open) void refetchWorkspaceFiles()
}}
disabled={disabled}
isLoading={loadingWorkspaceFiles}
Expand All @@ -763,7 +747,7 @@ export function FileUpload({
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
if (open) void refetchWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : 'Select or upload file'}
disabled={disabled || loadingWorkspaceFiles}
Expand Down
49 changes: 35 additions & 14 deletions apps/sim/blocks/blocks/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,23 +318,26 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
condition: { field: 'operation', value: 'file_write' },
mode: 'advanced',
},
{
id: 'appendFile',
title: 'File',
type: 'file-upload' as SubBlockType,
canonicalParamId: 'appendFileInput',
acceptedTypes: '.txt,.md,.json,.csv,.xml,.html,.htm,.yaml,.yml,.log,.rtf',
placeholder: 'Select or upload a workspace file',
mode: 'basic',
condition: { field: 'operation', value: 'file_append' },
required: { field: 'operation', value: 'file_append' },
},
{
id: 'appendFileName',
title: 'File',
type: 'dropdown' as SubBlockType,
placeholder: 'Select a workspace file...',
type: 'short-input' as SubBlockType,
canonicalParamId: 'appendFileInput',
placeholder: 'File name (e.g., notes.md)',
mode: 'advanced',
condition: { field: 'operation', value: 'file_append' },
required: { field: 'operation', value: 'file_append' },
options: [],
fetchOptions: async () => {
const { useWorkflowRegistry } = await import('@/stores/workflows/registry/store')
const workspaceId = useWorkflowRegistry.getState().hydration.workspaceId
if (!workspaceId) return []
const response = await fetch(`/api/workspaces/${workspaceId}/files`)
const data = await response.json()
if (!data.success || !data.files) return []
return data.files.map((f: { name: string }) => ({ label: f.name, id: f.name }))
},
},
{
id: 'appendContent',
Expand Down Expand Up @@ -362,8 +365,26 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
}

if (operation === 'file_append') {
const appendInput = params.appendFileInput
if (!appendInput) {
throw new Error('File is required for append')
}

let fileName: string
if (typeof appendInput === 'string') {
fileName = appendInput.trim()
} else {
const normalized = normalizeFileInput(appendInput, { single: true })
const file = normalized as Record<string, unknown> | null
fileName = (file?.name as string) ?? ''
}

if (!fileName) {
throw new Error('Could not determine file name')
}

return {
fileName: params.appendFileName,
fileName,
content: params.appendContent,
workspaceId: params._context?.workspaceId,
}
Expand Down Expand Up @@ -413,7 +434,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
fileName: { type: 'string', description: 'Name for a new file (write)' },
content: { type: 'string', description: 'File content to write' },
contentType: { type: 'string', description: 'MIME content type for write' },
appendFileName: { type: 'string', description: 'Name of existing file to append to' },
appendFileInput: { type: 'json', description: 'File to append to (canonical param)' },
appendContent: { type: 'string', description: 'Content to append to file' },
},
outputs: {
Expand Down
Loading