Spaces:
Sleeping
Sleeping
| import React, { useState } from 'react'; | |
| import type { Message } from 'ai'; | |
| import { toast } from 'react-toastify'; | |
| import { MAX_FILES, isBinaryFile, shouldIncludeFile } from '~/utils/fileUtils'; | |
| import { createChatFromFolder } from '~/utils/folderImport'; | |
| import { logStore } from '~/lib/stores/logs'; // Assuming logStore is imported from this location | |
| import { Button } from '~/components/ui/Button'; | |
| import { classNames } from '~/utils/classNames'; | |
| interface ImportFolderButtonProps { | |
| className?: string; | |
| importChat?: (description: string, messages: Message[]) => Promise<void>; | |
| } | |
| export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ className, importChat }) => { | |
| const [isLoading, setIsLoading] = useState(false); | |
| const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => { | |
| const allFiles = Array.from(e.target.files || []); | |
| const filteredFiles = allFiles.filter((file) => { | |
| const path = file.webkitRelativePath.split('/').slice(1).join('/'); | |
| const include = shouldIncludeFile(path); | |
| return include; | |
| }); | |
| if (filteredFiles.length === 0) { | |
| const error = new Error('No valid files found'); | |
| logStore.logError('File import failed - no valid files', error, { folderName: 'Unknown Folder' }); | |
| toast.error('No files found in the selected folder'); | |
| return; | |
| } | |
| if (filteredFiles.length > MAX_FILES) { | |
| const error = new Error(`Too many files: ${filteredFiles.length}`); | |
| logStore.logError('File import failed - too many files', error, { | |
| fileCount: filteredFiles.length, | |
| maxFiles: MAX_FILES, | |
| }); | |
| toast.error( | |
| `This folder contains ${filteredFiles.length.toLocaleString()} files. This product is not yet optimized for very large projects. Please select a folder with fewer than ${MAX_FILES.toLocaleString()} files.`, | |
| ); | |
| return; | |
| } | |
| const folderName = filteredFiles[0]?.webkitRelativePath.split('/')[0] || 'Unknown Folder'; | |
| setIsLoading(true); | |
| const loadingToast = toast.loading(`Importing ${folderName}...`); | |
| try { | |
| const fileChecks = await Promise.all( | |
| filteredFiles.map(async (file) => ({ | |
| file, | |
| isBinary: await isBinaryFile(file), | |
| })), | |
| ); | |
| const textFiles = fileChecks.filter((f) => !f.isBinary).map((f) => f.file); | |
| const binaryFilePaths = fileChecks | |
| .filter((f) => f.isBinary) | |
| .map((f) => f.file.webkitRelativePath.split('/').slice(1).join('/')); | |
| if (textFiles.length === 0) { | |
| const error = new Error('No text files found'); | |
| logStore.logError('File import failed - no text files', error, { folderName }); | |
| toast.error('No text files found in the selected folder'); | |
| return; | |
| } | |
| if (binaryFilePaths.length > 0) { | |
| logStore.logWarning(`Skipping binary files during import`, { | |
| folderName, | |
| binaryCount: binaryFilePaths.length, | |
| }); | |
| toast.info(`Skipping ${binaryFilePaths.length} binary files`); | |
| } | |
| const messages = await createChatFromFolder(textFiles, binaryFilePaths, folderName); | |
| if (importChat) { | |
| await importChat(folderName, [...messages]); | |
| } | |
| logStore.logSystem('Folder imported successfully', { | |
| folderName, | |
| textFileCount: textFiles.length, | |
| binaryFileCount: binaryFilePaths.length, | |
| }); | |
| toast.success('Folder imported successfully'); | |
| } catch (error) { | |
| logStore.logError('Failed to import folder', error, { folderName }); | |
| console.error('Failed to import folder:', error); | |
| toast.error('Failed to import folder'); | |
| } finally { | |
| setIsLoading(false); | |
| toast.dismiss(loadingToast); | |
| e.target.value = ''; // Reset file input | |
| } | |
| }; | |
| return ( | |
| <> | |
| <input | |
| type="file" | |
| id="folder-import" | |
| className="hidden" | |
| webkitdirectory="" | |
| directory="" | |
| onChange={handleFileChange} | |
| {...({} as any)} | |
| /> | |
| <Button | |
| onClick={() => { | |
| const input = document.getElementById('folder-import'); | |
| input?.click(); | |
| }} | |
| title="Import Folder" | |
| variant="default" | |
| size="lg" | |
| className={classNames( | |
| 'gap-2 bg-bolt-elements-background-depth-1', | |
| 'text-bolt-elements-textPrimary', | |
| 'hover:bg-bolt-elements-background-depth-2', | |
| 'border border-bolt-elements-borderColor', | |
| 'h-10 px-4 py-2 min-w-[120px] justify-center', | |
| 'transition-all duration-200 ease-in-out', | |
| className, | |
| )} | |
| disabled={isLoading} | |
| > | |
| <span className="i-ph:upload-simple w-4 h-4" /> | |
| {isLoading ? 'Importing...' : 'Import Folder'} | |
| </Button> | |
| </> | |
| ); | |
| }; | |