Merge pull request #5 from CAPTAIN320/export-files

Export files
This commit is contained in:
Yaqub Mahmoud 2025-01-28 17:15:27 +09:00 committed by GitHub
commit f25a3e469d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 84 additions and 2 deletions

View File

@ -16,6 +16,7 @@ import { cubicEasingFn } from '~/utils/easings';
import { renderLogger } from '~/utils/logger';
import { EditorPanel } from './EditorPanel';
import { Preview } from './Preview';
import JSZip from 'jszip';
interface WorkspaceProps {
chatStarted?: boolean;
@ -52,6 +53,53 @@ const workbenchVariants = {
},
} satisfies Variants;
async function downloadAsZip(files: Record<string, any>) {
const zip = new JSZip();
// add files to zip
for (const [path, dirent] of Object.entries(files)) {
if (dirent?.type === 'file' && dirent.content) {
zip.file(path.startsWith('/') ? path.slice(1) : path, dirent.content);
}
}
// generate and download zip
const blob = await zip.generateAsync({ type: 'blob' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'files.zip';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
async function copyFilesToDirectory(files: Record<string, any>) {
try {
// using the Fetch API to send files to your backend
const response = await fetch('/api/copy-files', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
files,
targetDirectory: '/Users/yaqub.mahmoud/github/ai-website-microservice/web-projects/project_name',
}),
});
if (!response.ok) {
throw new Error('Failed to copy files');
}
toast.success('Files copied successfully');
} catch (error) {
console.error('Error copying files:', error);
toast.error('Failed to copy files');
}
}
export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) => {
renderLogger.trace('Workbench');
@ -132,6 +180,14 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
Toggle Terminal
</PanelHeaderButton>
)}
<PanelHeaderButton className="mr-1 text-sm" onClick={() => downloadAsZip(files)}>
<div className="i-ph:download mr-1" />
Download ZIP
</PanelHeaderButton>
<PanelHeaderButton className="mr-1 text-sm" onClick={() => copyFilesToDirectory(files)}>
<div className="i-ph:rocket mr-1" />
Deploy
</PanelHeaderButton>
<IconButton
icon="i-ph:x-circle"
className="-mr-1"

View File

@ -62,6 +62,7 @@
"isbot": "^4.1.0",
"istextorbinary": "^9.5.0",
"jose": "^5.6.3",
"jszip": "^3.10.1",
"nanostores": "^0.10.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -90,7 +91,7 @@
"node-fetch": "^3.3.2",
"prettier": "^3.3.2",
"sass-embedded": "^1.83.4",
"typescript": "^5.5.2",
"typescript": "^5.7.3",
"unified": "^11.0.5",
"unocss": "^0.61.3",
"vite": "^5.3.1",

View File

@ -128,6 +128,9 @@ importers:
jose:
specifier: ^5.6.3
version: 5.9.6
jszip:
specifier: ^3.10.1
version: 3.10.1
nanostores:
specifier: ^0.10.3
version: 0.10.3
@ -208,7 +211,7 @@ importers:
specifier: ^1.83.4
version: 1.83.4
typescript:
specifier: ^5.5.2
specifier: ^5.7.3
version: 5.7.3
unified:
specifier: ^11.0.5
@ -3258,6 +3261,9 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
immutable@5.0.3:
resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
@ -3467,6 +3473,9 @@ packages:
jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@ -3481,6 +3490,9 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
@ -8878,6 +8890,8 @@ snapshots:
ignore@5.3.2: {}
immediate@3.0.6: {}
immutable@5.0.3: {}
import-fresh@3.3.0:
@ -9059,6 +9073,13 @@ snapshots:
optionalDependencies:
graceful-fs: 4.2.11
jszip@3.10.1:
dependencies:
lie: 3.3.0
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@ -9072,6 +9093,10 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
lie@3.3.0:
dependencies:
immediate: 3.0.6
lilconfig@3.1.3: {}
load-tsconfig@0.2.5: {}