waseemaofficial commited on
Commit
21dbd42
·
1 Parent(s): 4f7a06f

added download code button

Browse files
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
- <PanelHeaderButton
126
- className="mr-1 text-sm"
127
- onClick={() => {
128
- workbenchStore.toggleTerminal(!workbenchStore.showTerminal.get());
129
- }}
130
- >
131
- <div className="i-ph:terminal" />
132
- Toggle Terminal
133
- </PanelHeaderButton>
 
 
 
 
 
 
 
 
 
 
 
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
@@ -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
 
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
8426
+
8427
8428
  dependencies:
8429
  to-regex-range: 5.0.1
 
8775
 
8776
8777
 
8778
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