In this article, I will modify bolt.new to allow applications created in the tool to be downloaded locally. This feature will facilitate internal deployment of bolt.new applications, making it particularly useful for corporate environments.
In the next article, we will cover how to modify bolt.new to integrate with Azure OpenAI Service, streamlining the application for enterprise-level use cases. Please check it.
To enable the feature for downloading project files as a ZIP, we modify the file bolt.new/app/components/workbench/EditorPanel.tsx. The changes are marked between // Append start and // Append end for clarity.
... // Append start import JSZip from 'jszip'; // Append end ... export const EditorPanel = memo( ({ files, unsavedFiles, editorDocument, selectedFile, isStreaming, onFileSelect, onEditorChange, onEditorScroll, onFileSave, onFileReset, }: EditorPanelProps) => { ... // Append start const handleDownloadZip = useCallback(async () => { const zip = new JSZip(); const files = workbenchStore.files.get(); // Check if there are files to download if (Object.keys(files).length === 0) { toast.error('No files to download'); return; } try { // Add files to the ZIP, maintaining relative paths from WORK_DIR Object.entries(files).forEach(([path, content]) => { if (content && content.content) { const relativePath = path.startsWith(WORK_DIR) ? path.slice(WORK_DIR.length + 1) : path; zip.file(relativePath, content.content); } }); const zipBlob = await zip.generateAsync({ type: 'blob' }); const downloadLink = document.createElement('a'); downloadLink.href = URL.createObjectURL(zipBlob); // Use the project name from `package.json` if available const projectName = files[`${WORK_DIR}/package.json`]?.content ? JSON.parse(files[`${WORK_DIR}/package.json`].content).name : 'project'; downloadLink.download = `${projectName}.zip`; downloadLink.click(); URL.revokeObjectURL(downloadLink.href); toast.success('Files downloaded successfully'); } catch (error) { toast.error('Failed to create zip file'); console.error(error); } }, []); // Append end return ( <PanelGroup direction="vertical"> <Panel defaultSize={showTerminal ? DEFAULT_EDITOR_SIZE : 100} minSize={20}> <PanelGroup direction="horizontal"> <Panel defaultSize={20} minSize={10} collapsible> <div className="flex flex-col border-r border-bolt-elements-borderColor h-full"> <PanelHeader> <div className="i-ph:tree-structure-duotone shrink-0" /> Files {/* Append start */} <div className="flex-1" /> <button className="px-2 py-1 rounded-md text-bolt-elements-item-contentDefault bg-transparent enabled:hover:text-bolt-elements-item-contentActive enabled:hover:bg-bolt-elements-item-backgroundActive" onClick={handleDownloadZip} title="Download as ZIP" > <div className="i-ph:download-simple text-xl" /> </button> {/* Append end */} </PanelHeader> ...
Added JSZip import:
This library is used to generate the ZIP archive.
handleDownloadZip function:
Download button in the interface:
With this change, users can download the project files as a ZIP archive directly from the interface.
To use the JSZip library for generating ZIP archives, add it to the dependencies in package.json.
{ ... "dependencies": { "@ai-sdk/anthropic": "^0.0.39", // Append start "jszip": "^3.10.1", // Append end }, ... }
pnpm install
This ensures that the required library is available in your project for the ZIP generation functionality.
After completing the modifications, follow these steps to verify the new functionality:
Run the following commands to build and start bolt.new:
... // Append start import JSZip from 'jszip'; // Append end ... export const EditorPanel = memo( ({ files, unsavedFiles, editorDocument, selectedFile, isStreaming, onFileSelect, onEditorChange, onEditorScroll, onFileSave, onFileReset, }: EditorPanelProps) => { ... // Append start const handleDownloadZip = useCallback(async () => { const zip = new JSZip(); const files = workbenchStore.files.get(); // Check if there are files to download if (Object.keys(files).length === 0) { toast.error('No files to download'); return; } try { // Add files to the ZIP, maintaining relative paths from WORK_DIR Object.entries(files).forEach(([path, content]) => { if (content && content.content) { const relativePath = path.startsWith(WORK_DIR) ? path.slice(WORK_DIR.length + 1) : path; zip.file(relativePath, content.content); } }); const zipBlob = await zip.generateAsync({ type: 'blob' }); const downloadLink = document.createElement('a'); downloadLink.href = URL.createObjectURL(zipBlob); // Use the project name from `package.json` if available const projectName = files[`${WORK_DIR}/package.json`]?.content ? JSON.parse(files[`${WORK_DIR}/package.json`].content).name : 'project'; downloadLink.download = `${projectName}.zip`; downloadLink.click(); URL.revokeObjectURL(downloadLink.href); toast.success('Files downloaded successfully'); } catch (error) { toast.error('Failed to create zip file'); console.error(error); } }, []); // Append end return ( <PanelGroup direction="vertical"> <Panel defaultSize={showTerminal ? DEFAULT_EDITOR_SIZE : 100} minSize={20}> <PanelGroup direction="horizontal"> <Panel defaultSize={20} minSize={10} collapsible> <div className="flex flex-col border-r border-bolt-elements-borderColor h-full"> <PanelHeader> <div className="i-ph:tree-structure-duotone shrink-0" /> Files {/* Append start */} <div className="flex-1" /> <button className="px-2 py-1 rounded-md text-bolt-elements-item-contentDefault bg-transparent enabled:hover:text-bolt-elements-item-contentActive enabled:hover:bg-bolt-elements-item-backgroundActive" onClick={handleDownloadZip} title="Download as ZIP" > <div className="i-ph:download-simple text-xl" /> </button> {/* Append end */} </PanelHeader> ...
Open the Application in a Browser and navigate to the "Code" tab of the application interface.
Confirm that a download button is visible in the "Files" section, as shown below:
The above is the detailed content of Enabling Application Downloads in Local bolt.new. For more information, please follow other related articles on the PHP Chinese website!