mirror of
https://github.com/stackblitz/bolt.new
synced 2025-03-12 06:51:11 +00:00
Added parsing if ignore file and added handling of binary files
This commit is contained in:
parent
3d2ab89cdc
commit
050bf2028f
@ -1,22 +1,55 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { Message } from 'ai';
|
import type { Message } from 'ai';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
import ignore from 'ignore';
|
||||||
|
|
||||||
interface ImportFolderButtonProps {
|
interface ImportFolderButtonProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
importChat?: (description: string, messages: Message[]) => Promise<void>;
|
importChat?: (description: string, messages: Message[]) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IGNORED_FOLDERS = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '.cache', '.vscode', '.idea'];
|
// Common patterns to ignore, similar to .gitignore
|
||||||
|
const IGNORE_PATTERNS = [
|
||||||
|
'node_modules/**',
|
||||||
|
'.git/**',
|
||||||
|
'dist/**',
|
||||||
|
'build/**',
|
||||||
|
'.next/**',
|
||||||
|
'coverage/**',
|
||||||
|
'.cache/**',
|
||||||
|
'.vscode/**',
|
||||||
|
'.idea/**',
|
||||||
|
'**/*.log',
|
||||||
|
'**/.DS_Store',
|
||||||
|
'**/npm-debug.log*',
|
||||||
|
'**/yarn-debug.log*',
|
||||||
|
'**/yarn-error.log*',
|
||||||
|
];
|
||||||
|
|
||||||
|
const ig = ignore().add(IGNORE_PATTERNS);
|
||||||
const generateId = () => Math.random().toString(36).substring(2, 15);
|
const generateId = () => Math.random().toString(36).substring(2, 15);
|
||||||
|
|
||||||
|
const isBinaryFile = async (file: File): Promise<boolean> => {
|
||||||
|
const chunkSize = 1024; // Read the first 1 KB of the file
|
||||||
|
const buffer = new Uint8Array(await file.slice(0, chunkSize).arrayBuffer());
|
||||||
|
|
||||||
|
for (let i = 0; i < buffer.length; i++) {
|
||||||
|
const byte = buffer[i];
|
||||||
|
|
||||||
|
if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) {
|
||||||
|
return true; // Found a binary character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ className, importChat }) => {
|
export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ className, importChat }) => {
|
||||||
const shouldIncludeFile = (path: string): boolean => {
|
const shouldIncludeFile = (path: string): boolean => {
|
||||||
return !IGNORED_FOLDERS.some((folder) => path.includes(`/${folder}/`));
|
return !ig.ignores(path);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createChatFromFolder = async (files: File[]) => {
|
const createChatFromFolder = async (files: File[], binaryFiles: string[]) => {
|
||||||
const fileArtifacts = await Promise.all(
|
const fileArtifacts = await Promise.all(
|
||||||
files.map(async (file) => {
|
files.map(async (file) => {
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
@ -37,9 +70,14 @@ ${content}
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const binaryFilesMessage =
|
||||||
|
binaryFiles.length > 0
|
||||||
|
? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}`
|
||||||
|
: '';
|
||||||
|
|
||||||
const message: Message = {
|
const message: Message = {
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: `I'll help you set up these files.
|
content: `I'll help you set up these files.${binaryFilesMessage}
|
||||||
|
|
||||||
<boltArtifact id="imported-files" title="Imported Files">
|
<boltArtifact id="imported-files" title="Imported Files">
|
||||||
${fileArtifacts.join('\n\n')}
|
${fileArtifacts.join('\n\n')}
|
||||||
@ -74,8 +112,34 @@ ${fileArtifacts.join('\n\n')}
|
|||||||
const allFiles = Array.from(e.target.files || []);
|
const allFiles = Array.from(e.target.files || []);
|
||||||
const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath));
|
const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath));
|
||||||
|
|
||||||
|
if (filteredFiles.length === 0) {
|
||||||
|
toast.error('No files found in the selected folder');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createChatFromFolder(filteredFiles);
|
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`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await createChatFromFolder(textFiles, binaryFilePaths);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to import folder:', error);
|
console.error('Failed to import folder:', error);
|
||||||
toast.error('Failed to import folder');
|
toast.error('Failed to import folder');
|
||||||
|
@ -11,7 +11,7 @@ interface Logger {
|
|||||||
setLevel: (level: DebugLevel) => void;
|
setLevel: (level: DebugLevel) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentLevel: DebugLevel = (import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV) ? 'debug' : 'info';
|
let currentLevel: DebugLevel = import.meta.env.VITE_LOG_LEVEL ?? import.meta.env.DEV ? 'debug' : 'info';
|
||||||
|
|
||||||
const isWorker = 'HTMLRewriter' in globalThis;
|
const isWorker = 'HTMLRewriter' in globalThis;
|
||||||
const supportsColor = !isWorker;
|
const supportsColor = !isWorker;
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
"diff": "^5.2.0",
|
"diff": "^5.2.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"framer-motion": "^11.2.12",
|
"framer-motion": "^11.2.12",
|
||||||
|
"ignore": "^6.0.2",
|
||||||
"isbot": "^4.1.0",
|
"isbot": "^4.1.0",
|
||||||
"istextorbinary": "^9.5.0",
|
"istextorbinary": "^9.5.0",
|
||||||
"jose": "^5.6.3",
|
"jose": "^5.6.3",
|
||||||
|
@ -143,6 +143,9 @@ importers:
|
|||||||
framer-motion:
|
framer-motion:
|
||||||
specifier: ^11.2.12
|
specifier: ^11.2.12
|
||||||
version: 11.2.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 11.2.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
ignore:
|
||||||
|
specifier: ^6.0.2
|
||||||
|
version: 6.0.2
|
||||||
isbot:
|
isbot:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.4.0
|
version: 4.4.0
|
||||||
@ -3399,6 +3402,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
|
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
|
ignore@6.0.2:
|
||||||
|
resolution: {integrity: sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==}
|
||||||
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
immediate@3.0.6:
|
immediate@3.0.6:
|
||||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||||
|
|
||||||
@ -9290,6 +9297,8 @@ snapshots:
|
|||||||
|
|
||||||
ignore@5.3.1: {}
|
ignore@5.3.1: {}
|
||||||
|
|
||||||
|
ignore@6.0.2: {}
|
||||||
|
|
||||||
immediate@3.0.6: {}
|
immediate@3.0.6: {}
|
||||||
|
|
||||||
immutable@4.3.7: {}
|
immutable@4.3.7: {}
|
||||||
|
Loading…
Reference in New Issue
Block a user