forked from codinit-dev/codinit-dev
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathImportFolderButton.tsx
More file actions
135 lines (115 loc) · 4.71 KB
/
Copy pathImportFolderButton.tsx
File metadata and controls
135 lines (115 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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
interface ImportFolderButtonProps {
importChat?: (description: string, messages: Message[]) => Promise<void>;
}
export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ 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"
aria-label="Import Folder"
className="flex items-center gap-1.5 border border-codinit-elements-borderColor rounded-full px-3 py-1.5 text-xs transition-theme text-codinit-elements-textSecondary hover:text-codinit-elements-textPrimary bg-gray-50 dark:bg-gray-950 hover:bg-gray-100 dark:hover:bg-gray-900 disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isLoading}
>
<span
className={
isLoading
? 'w-4 h-4 block transition-theme i-ph:spinner animate-spin'
: 'w-4 h-4 block transition-theme i-ph:folder-open'
}
/>
<span className="transition-theme">{isLoading ? 'Importing...' : 'Import Folder'}</span>
</button>
</>
);
};