Files
bolt.new/app/components/header/HeaderActionButtons.client.tsx
Dustin Loring fa02511efb Merge pull request #6 from mnsdojo/chore/fix-typos-and-improve-code
fix: fixed typos and optimized code
2025-01-15 10:07:09 -05:00

110 lines
3.7 KiB
TypeScript

import JSZip from 'jszip';
import { chatStore } from '~/lib/stores/chat';
import { workbenchStore } from '~/lib/stores/workbench';
import { classNames } from '~/utils/classNames';
import { useStore } from '@nanostores/react';
import type { FileMap } from '~/lib/stores/files';
import { saveAs } from 'file-saver';
interface HeaderActionButtonsProps {}
export function HeaderActionButtons({}: HeaderActionButtonsProps) {
const showWorkbench = useStore(workbenchStore.showWorkbench);
const { showChat } = useStore(chatStore);
const files = useStore(workbenchStore.files) as FileMap;
const canHideChat = showWorkbench || !showChat;
const downloadZip = async () => {
const zip = new JSZip();
for (const [filePath, dirent] of Object.entries(files)) {
if (dirent?.type === 'file' && !dirent.isBinary) {
// remove '/home/project/' from the beginning of the path
const relativePath = filePath.replace(/^\/home\/project\//, '');
// split the path into segments
const pathSegments = relativePath.split('/');
// if there's more than one segment, we need to create folders
if (pathSegments.length > 1) {
let currentFolder = zip;
for (let i = 0; i < pathSegments.length - 1; i++) {
currentFolder = currentFolder.folder(pathSegments[i])!;
}
currentFolder.file(pathSegments[pathSegments.length - 1], dirent.content);
} else {
// if there's only one segment, it's a file in the root
zip.file(relativePath, dirent.content);
}
}
}
const content = await zip.generateAsync({ type: 'blob' });
saveAs(content, 'project.zip');
};
return (
<div className="flex gap-2">
<button
onClick={downloadZip}
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"
>
<div className="i-ph:download-bold" />
<span>Download</span>
</button>
<div className="flex border border-bolt-elements-borderColor rounded-md overflow-hidden">
<Button
active={showChat}
disabled={!canHideChat}
onClick={() => {
if (canHideChat) {
chatStore.setKey('showChat', !showChat);
}
}}
>
<div className="i-bolt:chat text-sm" />
</Button>
<div className="w-[1px] bg-bolt-elements-borderColor" />
<Button
active={showWorkbench}
onClick={() => {
if (showWorkbench && !showChat) {
chatStore.setKey('showChat', true);
}
workbenchStore.showWorkbench.set(!showWorkbench);
}}
>
<div className="i-ph:code-bold" />
</Button>
</div>
</div>
);
}
interface ButtonProps {
active?: boolean;
disabled?: boolean;
children?: React.ReactNode;
onClick?: VoidFunction;
}
function Button({ active = false, disabled = false, children, onClick }: ButtonProps) {
return (
<button
className={classNames('flex items-center p-1.5', {
'bg-bolt-elements-item-backgroundDefault hover:bg-bolt-elements-item-backgroundActive text-bolt-elements-textTertiary hover:text-bolt-elements-textPrimary':
!active,
'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent': active && !disabled,
'bg-bolt-elements-item-backgroundDefault text-alpha-gray-20 dark:text-alpha-white-20 cursor-not-allowed':
disabled,
})}
onClick={onClick}
>
{children}
</button>
);
}