mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
Fix ESLint issues
This commit is contained in:
@@ -572,8 +572,10 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
||||
<div className="flex flex-col justify-center gap-5">
|
||||
{!chatStarted && (
|
||||
<div className="flex justify-center gap-2">
|
||||
{ImportButtons(importChat)}
|
||||
<GitCloneButton importChat={importChat} />
|
||||
<div className="flex items-center gap-2">
|
||||
{ImportButtons(importChat)}
|
||||
<GitCloneButton importChat={importChat} className="min-w-[120px]" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!chatStarted &&
|
||||
|
||||
@@ -6,22 +6,21 @@ import { generateId } from '~/utils/fileUtils';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { LoadingOverlay } from '~/components/ui/LoadingOverlay';
|
||||
import type { IChatMetadata } from '~/lib/persistence';
|
||||
import { RepositorySelectionDialog } from '~/components/settings/connections/components/RepositorySelectionDialog';
|
||||
import { cn } from '~/lib/utils';
|
||||
import { Button } from '~/components/ui/Button';
|
||||
import type { IChatMetadata } from '~/lib/persistence/db';
|
||||
|
||||
const IGNORE_PATTERNS = [
|
||||
'node_modules/**',
|
||||
'.git/**',
|
||||
'.github/**',
|
||||
'.vscode/**',
|
||||
'**/*.jpg',
|
||||
'**/*.jpeg',
|
||||
'**/*.png',
|
||||
'dist/**',
|
||||
'build/**',
|
||||
'.next/**',
|
||||
'coverage/**',
|
||||
'.cache/**',
|
||||
'.vscode/**',
|
||||
'.idea/**',
|
||||
'**/*.log',
|
||||
'**/.DS_Store',
|
||||
@@ -34,51 +33,94 @@ const IGNORE_PATTERNS = [
|
||||
|
||||
const ig = ignore().add(IGNORE_PATTERNS);
|
||||
|
||||
const MAX_FILE_SIZE = 100 * 1024; // 100KB limit per file
|
||||
const MAX_TOTAL_SIZE = 500 * 1024; // 500KB total limit
|
||||
|
||||
interface GitCloneButtonProps {
|
||||
className?: string;
|
||||
importChat?: (description: string, messages: Message[], metadata?: IChatMetadata) => Promise<void>;
|
||||
}
|
||||
|
||||
export default function GitCloneButton({ importChat }: GitCloneButtonProps) {
|
||||
export default function GitCloneButton({ importChat, className }: GitCloneButtonProps) {
|
||||
const { ready, gitClone } = useGit();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
|
||||
const onClick = async (_e: any) => {
|
||||
const handleClone = async (repoUrl: string) => {
|
||||
if (!ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
const repoUrl = prompt('Enter the Git url');
|
||||
setLoading(true);
|
||||
|
||||
if (repoUrl) {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { workdir, data } = await gitClone(repoUrl);
|
||||
|
||||
try {
|
||||
const { workdir, data } = await gitClone(repoUrl);
|
||||
if (importChat) {
|
||||
const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
|
||||
if (importChat) {
|
||||
const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
|
||||
console.log(filePaths);
|
||||
let totalSize = 0;
|
||||
const skippedFiles: string[] = [];
|
||||
const fileContents = [];
|
||||
|
||||
const textDecoder = new TextDecoder('utf-8');
|
||||
for (const filePath of filePaths) {
|
||||
const { data: content, encoding } = data[filePath];
|
||||
|
||||
const fileContents = filePaths
|
||||
.map((filePath) => {
|
||||
const { data: content, encoding } = data[filePath];
|
||||
return {
|
||||
path: filePath,
|
||||
content:
|
||||
encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
|
||||
};
|
||||
})
|
||||
.filter((f) => f.content);
|
||||
// Skip binary files
|
||||
if (
|
||||
content instanceof Uint8Array &&
|
||||
!filePath.match(/\.(txt|md|js|jsx|ts|tsx|json|html|css|scss|less|yml|yaml|xml|svg)$/i)
|
||||
) {
|
||||
skippedFiles.push(filePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
const commands = await detectProjectCommands(fileContents);
|
||||
const commandsMessage = createCommandsMessage(commands);
|
||||
try {
|
||||
const textContent =
|
||||
encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '';
|
||||
|
||||
if (!textContent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check file size
|
||||
const fileSize = new TextEncoder().encode(textContent).length;
|
||||
|
||||
if (fileSize > MAX_FILE_SIZE) {
|
||||
skippedFiles.push(`${filePath} (too large: ${Math.round(fileSize / 1024)}KB)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check total size
|
||||
if (totalSize + fileSize > MAX_TOTAL_SIZE) {
|
||||
skippedFiles.push(`${filePath} (would exceed total size limit)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
totalSize += fileSize;
|
||||
fileContents.push({
|
||||
path: filePath,
|
||||
content: textContent,
|
||||
});
|
||||
} catch (e: any) {
|
||||
skippedFiles.push(`${filePath} (error: ${e.message})`);
|
||||
}
|
||||
}
|
||||
|
||||
const commands = await detectProjectCommands(fileContents);
|
||||
const commandsMessage = createCommandsMessage(commands);
|
||||
|
||||
const filesMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: `Cloning the repo ${repoUrl} into ${workdir}
|
||||
${
|
||||
skippedFiles.length > 0
|
||||
? `\nSkipped files (${skippedFiles.length}):
|
||||
${skippedFiles.map((f) => `- ${f}`).join('\n')}`
|
||||
: ''
|
||||
}
|
||||
|
||||
const filesMessage: Message = {
|
||||
role: 'assistant',
|
||||
content: `Cloning the repo ${repoUrl} into ${workdir}
|
||||
<boltArtifact id="imported-files" title="Git Cloned Files" type="bundled">
|
||||
${fileContents
|
||||
.map(
|
||||
@@ -89,37 +131,50 @@ ${escapeBoltTags(file.content)}
|
||||
)
|
||||
.join('\n')}
|
||||
</boltArtifact>`,
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
};
|
||||
id: generateId(),
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
const messages = [filesMessage];
|
||||
const messages = [filesMessage];
|
||||
|
||||
if (commandsMessage) {
|
||||
messages.push(commandsMessage);
|
||||
}
|
||||
|
||||
await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages, { gitUrl: repoUrl });
|
||||
if (commandsMessage) {
|
||||
messages.push(commandsMessage);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during import:', error);
|
||||
toast.error('Failed to import repository');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during import:', error);
|
||||
toast.error('Failed to import repository');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={onClick}
|
||||
<Button
|
||||
onClick={() => setIsDialogOpen(true)}
|
||||
title="Clone a Git Repo"
|
||||
className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className={cn(
|
||||
'gap-2 bg-[#F5F5F5] dark:bg-[#252525]',
|
||||
'text-bolt-elements-textPrimary dark:text-white',
|
||||
'hover:bg-[#E5E5E5] dark:hover:bg-[#333333]',
|
||||
'border-[#E5E5E5] dark:border-[#333333]',
|
||||
'h-10 px-4 py-2 min-w-[120px] justify-center',
|
||||
'transition-all duration-200 ease-in-out',
|
||||
className,
|
||||
)}
|
||||
disabled={!ready || loading}
|
||||
>
|
||||
<span className="i-ph:git-branch" />
|
||||
<span className="i-ph:git-branch w-4 h-4" />
|
||||
Clone a Git Repo
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
<RepositorySelectionDialog isOpen={isDialogOpen} onClose={() => setIsDialogOpen(false)} onSelect={handleClone} />
|
||||
|
||||
{loading && <LoadingOverlay message="Please wait while we clone the repository..." />}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,8 @@ 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 { cn } from '~/lib/utils';
|
||||
|
||||
interface ImportFolderButtonProps {
|
||||
className?: string;
|
||||
@@ -112,17 +114,27 @@ export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ classNam
|
||||
onChange={handleFileChange}
|
||||
{...({} as any)}
|
||||
/>
|
||||
<button
|
||||
<Button
|
||||
onClick={() => {
|
||||
const input = document.getElementById('folder-import');
|
||||
input?.click();
|
||||
}}
|
||||
className={className}
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className={cn(
|
||||
'gap-2 bg-[#F5F5F5] dark:bg-[#252525]',
|
||||
'text-bolt-elements-textPrimary dark:text-white',
|
||||
'hover:bg-[#E5E5E5] dark:hover:bg-[#333333]',
|
||||
'border-[#E5E5E5] dark:border-[#333333]',
|
||||
'h-10 px-4 py-2 min-w-[120px] justify-center',
|
||||
'transition-all duration-200 ease-in-out',
|
||||
className,
|
||||
)}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<div className="i-ph:upload-simple" />
|
||||
<span className="i-ph:upload-simple w-4 h-4" />
|
||||
{isLoading ? 'Importing...' : 'Import Folder'}
|
||||
</button>
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { Message } from 'ai';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ImportFolderButton } from '~/components/chat/ImportFolderButton';
|
||||
import { Button } from '~/components/ui/Button';
|
||||
import { cn } from '~/lib/utils';
|
||||
|
||||
type ChatData = {
|
||||
messages?: Message[]; // Standard Bolt format
|
||||
@@ -57,19 +59,35 @@ export function ImportButtons(importChat: ((description: string, messages: Messa
|
||||
/>
|
||||
<div className="flex flex-col items-center gap-4 max-w-2xl text-center">
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
<Button
|
||||
onClick={() => {
|
||||
const input = document.getElementById('chat-import');
|
||||
input?.click();
|
||||
}}
|
||||
className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className={cn(
|
||||
'gap-2 bg-[#F5F5F5] dark:bg-[#252525]',
|
||||
'text-bolt-elements-textPrimary dark:text-white',
|
||||
'hover:bg-[#E5E5E5] dark:hover:bg-[#333333]',
|
||||
'border-[#E5E5E5] dark:border-[#333333]',
|
||||
'h-10 px-4 py-2 min-w-[120px] justify-center',
|
||||
'transition-all duration-200 ease-in-out',
|
||||
)}
|
||||
>
|
||||
<div className="i-ph:upload-simple" />
|
||||
<span className="i-ph:upload-simple w-4 h-4" />
|
||||
Import Chat
|
||||
</button>
|
||||
</Button>
|
||||
<ImportFolderButton
|
||||
importChat={importChat}
|
||||
className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
|
||||
className={cn(
|
||||
'gap-2 bg-[#F5F5F5] dark:bg-[#252525]',
|
||||
'text-bolt-elements-textPrimary dark:text-white',
|
||||
'hover:bg-[#E5E5E5] dark:hover:bg-[#333333]',
|
||||
'border border-[#E5E5E5] dark:border-[#333333]',
|
||||
'h-10 px-4 py-2 min-w-[120px] justify-center',
|
||||
'transition-all duration-200 ease-in-out rounded-lg',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user