forked from stackblitz/bolt.new
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9b62edd
commit 5b7a2a5
Showing
3 changed files
with
216 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import ignore from 'ignore'; | ||
|
||
// Common patterns to ignore, similar to .gitignore | ||
export 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*', | ||
]; | ||
|
||
export const MAX_FILES = 1000; | ||
export const ig = ignore().add(IGNORE_PATTERNS); | ||
|
||
export const generateId = () => Math.random().toString(36).substring(2, 15); | ||
|
||
export const isBinaryFile = async (file: File): Promise<boolean> => { | ||
const chunkSize = 1024; | ||
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; | ||
} | ||
} | ||
return false; | ||
}; | ||
|
||
export const shouldIncludeFile = (path: string): boolean => { | ||
return !ig.ignores(path); | ||
}; | ||
|
||
const readPackageJson = async (files: File[]): Promise<{ scripts?: Record<string, string> } | null> => { | ||
const packageJsonFile = files.find(f => f.webkitRelativePath.endsWith('package.json')); | ||
if (!packageJsonFile) return null; | ||
|
||
try { | ||
const content = await new Promise<string>((resolve, reject) => { | ||
const reader = new FileReader(); | ||
reader.onload = () => resolve(reader.result as string); | ||
reader.onerror = reject; | ||
reader.readAsText(packageJsonFile); | ||
}); | ||
|
||
return JSON.parse(content); | ||
} catch (error) { | ||
console.error('Error reading package.json:', error); | ||
return null; | ||
} | ||
}; | ||
|
||
export const detectProjectType = async (files: File[]): Promise<{ type: string; setupCommand: string; followupMessage: string }> => { | ||
const hasFile = (name: string) => files.some(f => f.webkitRelativePath.endsWith(name)); | ||
|
||
if (hasFile('package.json')) { | ||
const packageJson = await readPackageJson(files); | ||
const scripts = packageJson?.scripts || {}; | ||
|
||
// Check for preferred commands in priority order | ||
const preferredCommands = ['dev', 'start', 'preview']; | ||
const availableCommand = preferredCommands.find(cmd => scripts[cmd]); | ||
|
||
if (availableCommand) { | ||
return { | ||
type: 'Node.js', | ||
setupCommand: `npm install && npm run ${availableCommand}`, | ||
followupMessage: `Found "${availableCommand}" script in package.json. Running "npm run ${availableCommand}" after installation.` | ||
}; | ||
} | ||
|
||
return { | ||
type: 'Node.js', | ||
setupCommand: 'npm install', | ||
followupMessage: 'Would you like me to inspect package.json to determine the available scripts for running this project?' | ||
}; | ||
} | ||
|
||
if (hasFile('index.html')) { | ||
return { | ||
type: 'Static', | ||
setupCommand: 'npx --yes serve', | ||
followupMessage: '' | ||
}; | ||
} | ||
|
||
return { type: '', setupCommand: '', followupMessage: '' }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import type { Message } from 'ai'; | ||
import { generateId, detectProjectType } from './fileUtils'; | ||
|
||
export const createChatFromFolder = async ( | ||
files: File[], | ||
binaryFiles: string[], | ||
folderName: string | ||
): Promise<{ userMessage: Message; assistantMessage: Message }> => { | ||
const fileArtifacts = await Promise.all( | ||
files.map(async (file) => { | ||
return new Promise<string>((resolve, reject) => { | ||
const reader = new FileReader(); | ||
reader.onload = () => { | ||
const content = reader.result as string; | ||
const relativePath = file.webkitRelativePath.split('/').slice(1).join('/'); | ||
resolve( | ||
`<boltAction type="file" filePath="${relativePath}"> | ||
${content} | ||
</boltAction>`, | ||
); | ||
}; | ||
reader.onerror = reject; | ||
reader.readAsText(file); | ||
}); | ||
}), | ||
); | ||
|
||
const project = await detectProjectType(files); | ||
const setupCommand = project.setupCommand ? `\n\n<boltAction type="shell">\n${project.setupCommand}\n</boltAction>` : ''; | ||
const followupMessage = project.followupMessage ? `\n\n${project.followupMessage}` : ''; | ||
|
||
const binaryFilesMessage = binaryFiles.length > 0 | ||
? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}` | ||
: ''; | ||
|
||
const assistantMessage: Message = { | ||
role: 'assistant', | ||
content: `I've imported the contents of the "${folderName}" folder.${binaryFilesMessage} | ||
<boltArtifact id="imported-files" title="Imported Files"> | ||
${fileArtifacts.join('\n\n')} | ||
${setupCommand} | ||
</boltArtifact>${followupMessage}`, | ||
id: generateId(), | ||
createdAt: new Date(), | ||
}; | ||
|
||
const userMessage: Message = { | ||
role: 'user', | ||
id: generateId(), | ||
content: `Import the "${folderName}" folder`, | ||
createdAt: new Date(), | ||
}; | ||
|
||
return { userMessage, assistantMessage }; | ||
}; |