Commit
·
21dbd42
1
Parent(s):
4f7a06f
added download code button
Browse files- app/components/workbench/Workbench.client.tsx +20 -9
- app/lib/stores/workbench.ts +32 -0
- package.json +3 -0
- pnpm-lock.yaml +41 -0
app/components/workbench/Workbench.client.tsx
CHANGED
@@ -122,15 +122,26 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
|
|
122 |
<Slider selected={selectedView} options={sliderOptions} setSelected={setSelectedView} />
|
123 |
<div className="ml-auto" />
|
124 |
{selectedView === 'code' && (
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
)}
|
135 |
<IconButton
|
136 |
icon="i-ph:x-circle"
|
|
|
122 |
<Slider selected={selectedView} options={sliderOptions} setSelected={setSelectedView} />
|
123 |
<div className="ml-auto" />
|
124 |
{selectedView === 'code' && (
|
125 |
+
<>
|
126 |
+
<PanelHeaderButton
|
127 |
+
className="mr-1 text-sm"
|
128 |
+
onClick={() => {
|
129 |
+
workbenchStore.downloadZip();
|
130 |
+
}}
|
131 |
+
>
|
132 |
+
<div className="i-ph:code" />
|
133 |
+
Download Code
|
134 |
+
</PanelHeaderButton>
|
135 |
+
<PanelHeaderButton
|
136 |
+
className="mr-1 text-sm"
|
137 |
+
onClick={() => {
|
138 |
+
workbenchStore.toggleTerminal(!workbenchStore.showTerminal.get());
|
139 |
+
}}
|
140 |
+
>
|
141 |
+
<div className="i-ph:terminal" />
|
142 |
+
Toggle Terminal
|
143 |
+
</PanelHeaderButton>
|
144 |
+
</>
|
145 |
)}
|
146 |
<IconButton
|
147 |
icon="i-ph:x-circle"
|
app/lib/stores/workbench.ts
CHANGED
@@ -9,6 +9,8 @@ import { EditorStore } from './editor';
|
|
9 |
import { FilesStore, type FileMap } from './files';
|
10 |
import { PreviewsStore } from './previews';
|
11 |
import { TerminalStore } from './terminal';
|
|
|
|
|
12 |
|
13 |
export interface ArtifactState {
|
14 |
id: string;
|
@@ -271,6 +273,36 @@ export class WorkbenchStore {
|
|
271 |
const artifacts = this.artifacts.get();
|
272 |
return artifacts[id];
|
273 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
274 |
}
|
275 |
|
276 |
export const workbenchStore = new WorkbenchStore();
|
|
|
9 |
import { FilesStore, type FileMap } from './files';
|
10 |
import { PreviewsStore } from './previews';
|
11 |
import { TerminalStore } from './terminal';
|
12 |
+
import JSZip from 'jszip';
|
13 |
+
import { saveAs } from 'file-saver';
|
14 |
|
15 |
export interface ArtifactState {
|
16 |
id: string;
|
|
|
273 |
const artifacts = this.artifacts.get();
|
274 |
return artifacts[id];
|
275 |
}
|
276 |
+
|
277 |
+
async downloadZip() {
|
278 |
+
const zip = new JSZip();
|
279 |
+
const files = this.files.get();
|
280 |
+
|
281 |
+
for (const [filePath, dirent] of Object.entries(files)) {
|
282 |
+
if (dirent?.type === 'file' && !dirent.isBinary) {
|
283 |
+
// Remove '/home/project/' from the beginning of the path
|
284 |
+
const relativePath = filePath.replace(/^\/home\/project\//, '');
|
285 |
+
|
286 |
+
// Split the path into segments
|
287 |
+
const pathSegments = relativePath.split('/');
|
288 |
+
|
289 |
+
// If there's more than one segment, we need to create folders
|
290 |
+
if (pathSegments.length > 1) {
|
291 |
+
let currentFolder = zip;
|
292 |
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
293 |
+
currentFolder = currentFolder.folder(pathSegments[i])!;
|
294 |
+
}
|
295 |
+
currentFolder.file(pathSegments[pathSegments.length - 1], dirent.content);
|
296 |
+
} else {
|
297 |
+
// If there's only one segment, it's a file in the root
|
298 |
+
zip.file(relativePath, dirent.content);
|
299 |
+
}
|
300 |
+
}
|
301 |
+
}
|
302 |
+
|
303 |
+
const content = await zip.generateAsync({ type: 'blob' });
|
304 |
+
saveAs(content, 'project.zip');
|
305 |
+
}
|
306 |
}
|
307 |
|
308 |
export const workbenchStore = new WorkbenchStore();
|
package.json
CHANGED
@@ -59,10 +59,12 @@
|
|
59 |
"ai": "^3.4.9",
|
60 |
"date-fns": "^3.6.0",
|
61 |
"diff": "^5.2.0",
|
|
|
62 |
"framer-motion": "^11.2.12",
|
63 |
"isbot": "^4.1.0",
|
64 |
"istextorbinary": "^9.5.0",
|
65 |
"jose": "^5.6.3",
|
|
|
66 |
"nanostores": "^0.10.3",
|
67 |
"ollama-ai-provider": "^0.15.2",
|
68 |
"react": "^18.2.0",
|
@@ -84,6 +86,7 @@
|
|
84 |
"@cloudflare/workers-types": "^4.20240620.0",
|
85 |
"@remix-run/dev": "^2.10.0",
|
86 |
"@types/diff": "^5.2.1",
|
|
|
87 |
"@types/react": "^18.2.20",
|
88 |
"@types/react-dom": "^18.2.7",
|
89 |
"fast-glob": "^3.3.2",
|
|
|
59 |
"ai": "^3.4.9",
|
60 |
"date-fns": "^3.6.0",
|
61 |
"diff": "^5.2.0",
|
62 |
+
"file-saver": "^2.0.5",
|
63 |
"framer-motion": "^11.2.12",
|
64 |
"isbot": "^4.1.0",
|
65 |
"istextorbinary": "^9.5.0",
|
66 |
"jose": "^5.6.3",
|
67 |
+
"jszip": "^3.10.1",
|
68 |
"nanostores": "^0.10.3",
|
69 |
"ollama-ai-provider": "^0.15.2",
|
70 |
"react": "^18.2.0",
|
|
|
86 |
"@cloudflare/workers-types": "^4.20240620.0",
|
87 |
"@remix-run/dev": "^2.10.0",
|
88 |
"@types/diff": "^5.2.1",
|
89 |
+
"@types/file-saver": "^2.0.7",
|
90 |
"@types/react": "^18.2.20",
|
91 |
"@types/react-dom": "^18.2.7",
|
92 |
"fast-glob": "^3.3.2",
|
pnpm-lock.yaml
CHANGED
@@ -119,6 +119,9 @@ importers:
|
|
119 |
diff:
|
120 |
specifier: ^5.2.0
|
121 |
version: 5.2.0
|
|
|
|
|
|
|
122 |
framer-motion:
|
123 |
specifier: ^11.2.12
|
124 |
version: 11.2.12([email protected]([email protected]))([email protected])
|
@@ -131,6 +134,9 @@ importers:
|
|
131 |
jose:
|
132 |
specifier: ^5.6.3
|
133 |
version: 5.6.3
|
|
|
|
|
|
|
134 |
nanostores:
|
135 |
specifier: ^0.10.3
|
136 |
version: 0.10.3
|
@@ -189,6 +195,9 @@ importers:
|
|
189 |
'@types/diff':
|
190 |
specifier: ^5.2.1
|
191 |
version: 5.2.1
|
|
|
|
|
|
|
192 |
'@types/react':
|
193 |
specifier: ^18.2.20
|
194 |
version: 18.3.3
|
@@ -1772,6 +1781,9 @@ packages:
|
|
1772 |
'@types/[email protected]':
|
1773 |
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
|
1774 |
|
|
|
|
|
|
|
1775 |
'@types/[email protected]':
|
1776 |
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
|
1777 |
|
@@ -2900,6 +2912,9 @@ packages:
|
|
2900 |
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
2901 |
engines: {node: '>=16.0.0'}
|
2902 |
|
|
|
|
|
|
|
2903 | |
2904 |
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
2905 |
engines: {node: '>=8'}
|
@@ -3181,6 +3196,9 @@ packages:
|
|
3181 |
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
|
3182 |
engines: {node: '>= 4'}
|
3183 |
|
|
|
|
|
|
|
3184 | |
3185 |
resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
|
3186 |
|
@@ -3396,6 +3414,9 @@ packages:
|
|
3396 | |
3397 |
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
3398 |
|
|
|
|
|
|
|
3399 | |
3400 |
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
3401 |
|
@@ -3410,6 +3431,9 @@ packages:
|
|
3410 |
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
3411 |
engines: {node: '>= 0.8.0'}
|
3412 |
|
|
|
|
|
|
|
3413 | |
3414 |
resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
|
3415 |
engines: {node: '>=14'}
|
@@ -6912,6 +6936,8 @@ snapshots:
|
|
6912 |
|
6913 |
'@types/[email protected]': {}
|
6914 |
|
|
|
|
|
6915 |
'@types/[email protected]':
|
6916 |
dependencies:
|
6917 |
'@types/unist': 2.0.10
|
@@ -8396,6 +8422,8 @@ snapshots:
|
|
8396 |
dependencies:
|
8397 |
flat-cache: 4.0.1
|
8398 |
|
|
|
|
|
8399 | |
8400 |
dependencies:
|
8401 |
to-regex-range: 5.0.1
|
@@ -8747,6 +8775,8 @@ snapshots:
|
|
8747 |
|
8748 | |
8749 |
|
|
|
|
|
8750 | |
8751 |
optional: true
|
8752 |
|
@@ -8915,6 +8945,13 @@ snapshots:
|
|
8915 |
optionalDependencies:
|
8916 |
graceful-fs: 4.2.11
|
8917 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8918 | |
8919 |
dependencies:
|
8920 |
json-buffer: 3.0.1
|
@@ -8928,6 +8965,10 @@ snapshots:
|
|
8928 |
prelude-ls: 1.2.1
|
8929 |
type-check: 0.4.0
|
8930 |
|
|
|
|
|
|
|
|
|
8931 | |
8932 |
|
8933 |
|
|
119 |
diff:
|
120 |
specifier: ^5.2.0
|
121 |
version: 5.2.0
|
122 |
+
file-saver:
|
123 |
+
specifier: ^2.0.5
|
124 |
+
version: 2.0.5
|
125 |
framer-motion:
|
126 |
specifier: ^11.2.12
|
127 |
version: 11.2.12([email protected]([email protected]))([email protected])
|
|
|
134 |
jose:
|
135 |
specifier: ^5.6.3
|
136 |
version: 5.6.3
|
137 |
+
jszip:
|
138 |
+
specifier: ^3.10.1
|
139 |
+
version: 3.10.1
|
140 |
nanostores:
|
141 |
specifier: ^0.10.3
|
142 |
version: 0.10.3
|
|
|
195 |
'@types/diff':
|
196 |
specifier: ^5.2.1
|
197 |
version: 5.2.1
|
198 |
+
'@types/file-saver':
|
199 |
+
specifier: ^2.0.7
|
200 |
+
version: 2.0.7
|
201 |
'@types/react':
|
202 |
specifier: ^18.2.20
|
203 |
version: 18.3.3
|
|
|
1781 |
'@types/[email protected]':
|
1782 |
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
|
1783 |
|
1784 |
+
'@types/[email protected]':
|
1785 |
+
resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
|
1786 |
+
|
1787 |
'@types/[email protected]':
|
1788 |
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
|
1789 |
|
|
|
2912 |
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
2913 |
engines: {node: '>=16.0.0'}
|
2914 |
|
2915 | |
2916 |
+
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
2917 |
+
|
2918 | |
2919 |
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
2920 |
engines: {node: '>=8'}
|
|
|
3196 |
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
|
3197 |
engines: {node: '>= 4'}
|
3198 |
|
3199 | |
3200 |
+
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
3201 |
+
|
3202 | |
3203 |
resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
|
3204 |
|
|
|
3414 | |
3415 |
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
3416 |
|
3417 | |
3418 |
+
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
3419 |
+
|
3420 | |
3421 |
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
3422 |
|
|
|
3431 |
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
3432 |
engines: {node: '>= 0.8.0'}
|
3433 |
|
3434 | |
3435 |
+
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
3436 |
+
|
3437 | |
3438 |
resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
|
3439 |
engines: {node: '>=14'}
|
|
|
6936 |
|
6937 |
'@types/[email protected]': {}
|
6938 |
|
6939 |
+
'@types/[email protected]': {}
|
6940 |
+
|
6941 |
'@types/[email protected]':
|
6942 |
dependencies:
|
6943 |
'@types/unist': 2.0.10
|
|
|
8422 |
dependencies:
|
8423 |
flat-cache: 4.0.1
|
8424 |
|
8425 |
+
[email protected]: {}
|
8426 |
+
|
8427 | |
8428 |
dependencies:
|
8429 |
to-regex-range: 5.0.1
|
|
|
8775 |
|
8776 | |
8777 |
|
8778 |
+
[email protected]: {}
|
8779 |
+
|
8780 | |
8781 |
optional: true
|
8782 |
|
|
|
8945 |
optionalDependencies:
|
8946 |
graceful-fs: 4.2.11
|
8947 |
|
8948 | |
8949 |
+
dependencies:
|
8950 |
+
lie: 3.3.0
|
8951 |
+
pako: 1.0.11
|
8952 |
+
readable-stream: 2.3.8
|
8953 |
+
setimmediate: 1.0.5
|
8954 |
+
|
8955 | |
8956 |
dependencies:
|
8957 |
json-buffer: 3.0.1
|
|
|
8965 |
prelude-ls: 1.2.1
|
8966 |
type-check: 0.4.0
|
8967 |
|
8968 | |
8969 |
+
dependencies:
|
8970 |
+
immediate: 3.0.6
|
8971 |
+
|
8972 | |
8973 |
|
8974 |