|
1 |
| -import { useStore } from '@nanostores/react'; |
| 1 | +import JSZip from 'jszip'; |
2 | 2 | import { chatStore } from '~/lib/stores/chat';
|
3 | 3 | import { workbenchStore } from '~/lib/stores/workbench';
|
4 | 4 | import { classNames } from '~/utils/classNames';
|
| 5 | +import { useStore } from '@nanostores/react'; |
| 6 | +import type { FileMap } from '~/lib/stores/files'; |
| 7 | +import { saveAs } from 'file-saver'; |
5 | 8 |
|
6 | 9 | interface HeaderActionButtonsProps {}
|
7 | 10 |
|
8 | 11 | export function HeaderActionButtons({}: HeaderActionButtonsProps) {
|
9 | 12 | const showWorkbench = useStore(workbenchStore.showWorkbench);
|
10 | 13 | const { showChat } = useStore(chatStore);
|
| 14 | + const files = useStore(workbenchStore.files) as FileMap; |
11 | 15 |
|
12 | 16 | const canHideChat = showWorkbench || !showChat;
|
13 | 17 |
|
| 18 | + const downloadZip = async () => { |
| 19 | + const zip = new JSZip(); |
| 20 | + |
| 21 | + for (const [filePath, dirent] of Object.entries(files)) { |
| 22 | + if (dirent?.type === 'file' && !dirent.isBinary) { |
| 23 | + // remove '/home/project/' from the beginning of the path |
| 24 | + const relativePath = filePath.replace(/^\/home\/project\//, ''); |
| 25 | + |
| 26 | + // split the path into segments |
| 27 | + const pathSegments = relativePath.split('/'); |
| 28 | + |
| 29 | + // if there's more than one segment, we need to create folders |
| 30 | + if (pathSegments.length > 1) { |
| 31 | + let currentFolder = zip; |
| 32 | + |
| 33 | + for (let i = 0; i < pathSegments.length - 1; i++) { |
| 34 | + currentFolder = currentFolder.folder(pathSegments[i])!; |
| 35 | + } |
| 36 | + currentFolder.file(pathSegments[pathSegments.length - 1], dirent.content); |
| 37 | + } else { |
| 38 | + // if there's only one segment, it's a file in the root |
| 39 | + zip.file(relativePath, dirent.content); |
| 40 | + } |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + const content = await zip.generateAsync({ type: 'blob' }); |
| 45 | + saveAs(content, 'project.zip'); |
| 46 | + }; |
| 47 | + |
14 | 48 | return (
|
15 |
| - <div className="flex"> |
| 49 | + <div className="flex gap-2"> |
| 50 | + <button |
| 51 | + onClick={downloadZip} |
| 52 | + className="rounded-md items-center justify-center outline-accent-600 px-3 py-1.25 text-xs bg-[#232323] text-bolt-elements-button-secondary-text enabled:hover:bg-bolt-elements-button-secondary-backgroundHover flex gap-1.7" |
| 53 | + > |
| 54 | + <div className="i-ph:download-bold" /> |
| 55 | + <span>Download</span> |
| 56 | + </button> |
16 | 57 | <div className="flex border border-bolt-elements-borderColor rounded-md overflow-hidden">
|
17 | 58 | <Button
|
18 | 59 | active={showChat}
|
|
0 commit comments