Home >Web Front-end >JS Tutorial >Enabling Application Downloads in Local bolt.new

Enabling Application Downloads in Local bolt.new

Linda Hamilton
Linda HamiltonOriginal
2024-12-05 09:06:11368browse

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.

Objective

  • Add functionality to download the project files as a ZIP archive.

Steps to Implement

  1. Integrate a download button in the interface
    • Add a download button in the sidebar or toolbar.
  2. Generate a ZIP archive of the project
    • Use a library like JSZip to bundle project files into a ZIP archive.
  3. Download the ZIP archive
    • Trigger the browser's download functionality with the generated ZIP file.
  4. Test the feature
    • Ensure that the downloaded ZIP contains the expected files and directory structure.

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.

Adding a Download Button

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>  
    ...  

Explanation of Changes

  1. Added JSZip import:

    This library is used to generate the ZIP archive.

  2. handleDownloadZip function:

    • Retrieves files from the workbenchStore.
    • Creates a ZIP file with relative paths from WORK_DIR.
    • Automatically names the ZIP file using the project name from package.json, if available.
  3. Download button in the interface:

    • A button is added to the Files panel header to trigger the ZIP download process.
    • Styled and includes a tooltip for user clarity.

With this change, users can download the project files as a ZIP archive directly from the interface.

Updating package.json

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  
  },  
  ...  
}  

Steps to Apply the Update

  1. Open the package.json file.
  2. Add "jszip": "^3.10.1" under the "dependencies" section as shown above.
  3. Run the following command to install the library:
pnpm install  

This ensures that the required library is available in your project for the ZIP generation functionality.

Testing the Download Feature

After completing the modifications, follow these steps to verify the new functionality:

Start the Application

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>  
    ...  
  1. Open the Application in a Browser and navigate to the "Code" tab of the application interface.

  2. Confirm that a download button is visible in the "Files" section, as shown below:

Enabling Application Downloads in Local bolt.new

  1. Download the Project Files: Click the download button and download the ZIP file.

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn