import { useCallback } from 'react' import { useDropzone } from 'react-dropzone' import { ImportingModel, baseName, fs, joinPath } from '@janhq/core' import { Modal, ModalContent, ModalHeader, ModalTitle } from '@janhq/uikit' import { useAtomValue, useSetAtom } from 'jotai' import { UploadCloudIcon } from 'lucide-react' import { v4 as uuidv4 } from 'uuid' import { snackbar } from '@/containers/Toast' import useDropModelBinaries from '@/hooks/useDropModelBinaries' import { getImportModelStageAtom, setImportModelStageAtom, } from '@/hooks/useImportModel' import { FilePathWithSize } from '@/utils/file' import { importingModelsAtom } from '@/helpers/atoms/Model.atom' const SelectingModelModal: React.FC = () => { const setImportModelStage = useSetAtom(setImportModelStageAtom) const importModelStage = useAtomValue(getImportModelStageAtom) const setImportingModels = useSetAtom(importingModelsAtom) const { onDropModels } = useDropModelBinaries() const onSelectFileClick = useCallback(async () => { const filePaths = await window.core?.api?.selectModelFiles() if (!filePaths || filePaths.length === 0) return const sanitizedFilePaths: FilePathWithSize[] = [] for (const filePath of filePaths) { const fileStats = await fs.fileStat(filePath, true) if (!fileStats) continue if (!fileStats.isDirectory) { const fileName = await baseName(filePath) sanitizedFilePaths.push({ path: filePath, name: fileName, size: fileStats.size, }) } else { // allowing only one level of directory const files = await fs.readdirSync(filePath) for (const file of files) { const fullPath = await joinPath([filePath, file]) const fileStats = await fs.fileStat(fullPath, true) if (!fileStats || fileStats.isDirectory) continue sanitizedFilePaths.push({ path: fullPath, name: file, size: fileStats.size, }) } } } const unsupportedFiles = sanitizedFilePaths.filter( (file) => !file.path.endsWith('.gguf') ) const supportedFiles = sanitizedFilePaths.filter((file) => file.path.endsWith('.gguf') ) const importingModels: ImportingModel[] = supportedFiles.map( ({ path, name, size }: FilePathWithSize) => { return { importId: uuidv4(), modelId: undefined, name: name.replace('.gguf', ''), description: '', path: path, tags: [], size: size, status: 'PREPARING', format: 'gguf', } } ) if (unsupportedFiles.length > 0) { snackbar({ description: `Only files with .gguf extension can be imported.`, type: 'error', }) } if (importingModels.length === 0) return setImportingModels(importingModels) setImportModelStage('MODEL_SELECTED') }, [setImportingModels, setImportModelStage]) const { isDragActive, getRootProps } = useDropzone({ noClick: true, multiple: true, onDrop: onDropModels, }) const borderColor = isDragActive ? 'border-primary' : 'border-[#F4F4F5]' const textColor = isDragActive ? 'text-blue-600' : 'text-[#71717A]' const dragAndDropBgColor = isDragActive ? 'bg-[#EFF6FF]' : 'bg-white' return ( { setImportModelStage('NONE') }} > Import Model

Import any model file (GGUF) or folder. Your imported model will be private to you.

Click to upload {' '} or drag and drop
(GGUF)
) } export default SelectingModelModal