2024-11-26 08:18:46 +00:00
import React , { useState } from 'react' ;
2024-11-25 08:24:03 +00:00
import type { Message } from 'ai' ;
import { toast } from 'react-toastify' ;
2024-12-08 10:16:09 +00:00
import { MAX_FILES , isBinaryFile , shouldIncludeFile } from '~/utils/fileUtils' ;
import { createChatFromFolder } from '~/utils/folderImport' ;
2024-11-25 08:24:03 +00:00
interface ImportFolderButtonProps {
className? : string ;
importChat ? : ( description : string , messages : Message [ ] ) = > Promise < void > ;
}
export const ImportFolderButton : React.FC < ImportFolderButtonProps > = ( { className , importChat } ) = > {
2024-11-26 08:18:46 +00:00
const [ isLoading , setIsLoading ] = useState ( false ) ;
2024-11-25 17:50:01 +00:00
2024-11-26 08:18:46 +00:00
const handleFileChange = async ( e : React.ChangeEvent < HTMLInputElement > ) = > {
const allFiles = Array . from ( e . target . files || [ ] ) ;
2024-11-25 08:24:03 +00:00
2024-11-26 08:18:46 +00:00
if ( allFiles . length > MAX_FILES ) {
toast . error (
2024-12-08 10:16:09 +00:00
` This folder contains ${ allFiles . length . toLocaleString ( ) } files. This product is not yet optimized for very large projects. Please select a folder with fewer than ${ MAX_FILES . toLocaleString ( ) } files. ` ,
2024-11-26 08:18:46 +00:00
) ;
return ;
}
2024-12-08 10:16:09 +00:00
2024-11-26 08:18:46 +00:00
const folderName = allFiles [ 0 ] ? . webkitRelativePath . split ( '/' ) [ 0 ] || 'Unknown Folder' ;
setIsLoading ( true ) ;
2024-12-08 10:16:09 +00:00
2024-11-26 08:18:46 +00:00
const loadingToast = toast . loading ( ` Importing ${ folderName } ... ` ) ;
try {
const filteredFiles = allFiles . filter ( ( file ) = > shouldIncludeFile ( file . webkitRelativePath ) ) ;
if ( filteredFiles . length === 0 ) {
toast . error ( 'No files found in the selected folder' ) ;
return ;
}
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 ) {
toast . error ( 'No text files found in the selected folder' ) ;
return ;
}
if ( binaryFilePaths . length > 0 ) {
toast . info ( ` Skipping ${ binaryFilePaths . length } binary files ` ) ;
}
2024-12-07 22:42:29 +00:00
const messages = await createChatFromFolder ( textFiles , binaryFilePaths , folderName ) ;
2024-12-02 18:21:59 +00:00
2024-11-26 08:18:46 +00:00
if ( importChat ) {
2024-12-07 22:42:29 +00:00
await importChat ( folderName , [ . . . messages ] ) ;
2024-11-26 08:18:46 +00:00
}
2024-12-02 18:21:59 +00:00
2024-11-26 08:18:46 +00:00
toast . success ( 'Folder imported successfully' ) ;
} catch ( error ) {
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
2024-11-25 08:24:03 +00:00
}
} ;
return (
< >
< input
type = "file"
id = "folder-import"
className = "hidden"
webkitdirectory = ""
directory = ""
2024-11-26 08:18:46 +00:00
onChange = { handleFileChange }
{ . . . ( { } as any ) }
2024-11-25 08:24:03 +00:00
/ >
< button
onClick = { ( ) = > {
const input = document . getElementById ( 'folder-import' ) ;
input ? . click ( ) ;
} }
className = { className }
2024-11-26 08:18:46 +00:00
disabled = { isLoading }
2024-11-25 08:24:03 +00:00
>
2024-12-01 14:13:16 +00:00
< div className = "i-ph:upload-simple" / >
2024-11-26 08:18:46 +00:00
{ isLoading ? 'Importing...' : 'Import Folder' }
2024-11-25 08:24:03 +00:00
< / button >
< / >
) ;
} ;