enzostvs HF Staff commited on
Commit
7bf32d5
·
1 Parent(s): 09841e5

Download project as Zip

Browse files
app/api/me/projects/[namespace]/[repoId]/download/route.ts ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { RepoDesignation, listFiles, spaceInfo, downloadFile } from "@huggingface/hub";
3
+ import JSZip from "jszip";
4
+
5
+ import { isAuthenticated } from "@/lib/auth";
6
+
7
+ export async function GET(
8
+ req: NextRequest,
9
+ { params }: { params: Promise<{ namespace: string; repoId: string }> }
10
+ ) {
11
+ const user = await isAuthenticated();
12
+
13
+ if (user instanceof NextResponse || !user) {
14
+ return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
15
+ }
16
+
17
+ const param = await params;
18
+ const { namespace, repoId } = param;
19
+
20
+ try {
21
+ const space = await spaceInfo({
22
+ name: `${namespace}/${repoId}`,
23
+ accessToken: user.token as string,
24
+ additionalFields: ["author"],
25
+ });
26
+
27
+ if (!space || space.sdk !== "static") {
28
+ return NextResponse.json(
29
+ {
30
+ ok: false,
31
+ error: "Space is not a static space",
32
+ },
33
+ { status: 404 }
34
+ );
35
+ }
36
+
37
+ if (space.author !== user.name) {
38
+ return NextResponse.json(
39
+ {
40
+ ok: false,
41
+ error: "Space does not belong to the authenticated user",
42
+ },
43
+ { status: 403 }
44
+ );
45
+ }
46
+
47
+ const repo: RepoDesignation = {
48
+ type: "space",
49
+ name: `${namespace}/${repoId}`,
50
+ };
51
+
52
+ // Create a new JSZip instance
53
+ const zip = new JSZip();
54
+
55
+ // Iterate through all files in the repo
56
+ for await (const fileInfo of listFiles({
57
+ repo,
58
+ accessToken: user.token as string
59
+ })) {
60
+ // Skip directories and hidden files
61
+ if (fileInfo.type === "directory" || fileInfo.path.startsWith(".")) {
62
+ continue;
63
+ }
64
+
65
+ try {
66
+ // Download the file
67
+ const blob = await downloadFile({
68
+ repo,
69
+ accessToken: user.token as string,
70
+ path: fileInfo.path,
71
+ raw: true
72
+ });
73
+
74
+ if (blob) {
75
+ // Add file to zip
76
+ const arrayBuffer = await blob.arrayBuffer();
77
+ zip.file(fileInfo.path, arrayBuffer);
78
+ }
79
+ } catch (error) {
80
+ console.error(`Error downloading file ${fileInfo.path}:`, error);
81
+ // Continue with other files even if one fails
82
+ }
83
+ }
84
+
85
+ // Generate the ZIP file
86
+ const zipBuffer = await zip.generateAsync({
87
+ type: "nodebuffer",
88
+ compression: "DEFLATE",
89
+ compressionOptions: {
90
+ level: 6
91
+ }
92
+ });
93
+
94
+ // Create the filename from the project name
95
+ const projectName = `${namespace}-${repoId}`.replace(/[^a-zA-Z0-9-_]/g, '_');
96
+ const filename = `${projectName}.zip`;
97
+
98
+ // Return the ZIP file
99
+ return new NextResponse(zipBuffer, {
100
+ headers: {
101
+ "Content-Type": "application/zip",
102
+ "Content-Disposition": `attachment; filename="${filename}"`,
103
+ "Content-Length": zipBuffer.length.toString(),
104
+ },
105
+ });
106
+ } catch (error: any) {
107
+ console.error("Error creating ZIP:", error);
108
+ return NextResponse.json(
109
+ { ok: false, error: error.message || "Failed to create ZIP file" },
110
+ { status: 500 }
111
+ );
112
+ }
113
+ }
114
+
components/editor/preview/index.tsx CHANGED
@@ -767,19 +767,19 @@ export const Preview = ({
767
  </div>
768
  )}
769
  {!isNew && !currentCommit && (
770
- <div className="top-0 left-0 right-0 z-20 bg-amber-500/90 backdrop-blur-sm border-b border-amber-600 px-4 py-2 flex items-center justify-between gap-3 text-sm w-full">
771
  <div className="flex items-center gap-2 flex-1">
772
- <TriangleAlert className="size-4 text-amber-900 flex-shrink-0" />
773
- <span className="text-amber-900 font-medium">
774
  Preview version of the project. Try refreshing the preview if
775
  you experience any issues.
776
  </span>
777
  </div>
778
  <button
779
  onClick={refreshIframe}
780
- className="cursor-pointer text-xs px-3 py-1 bg-amber-900 hover:bg-amber-800 text-amber-50 rounded-md font-medium transition-colors whitespace-nowrap flex items-center gap-1.5"
781
  >
782
- <RefreshCcw className="size-3 text-amber-50 flex-shrink-0" />
783
  Refresh
784
  </button>
785
  </div>
 
767
  </div>
768
  )}
769
  {!isNew && !currentCommit && (
770
+ <div className="top-0 left-0 right-0 z-20 bg-neutral-900/95 backdrop-blur-sm border-b border-neutral-800 px-4 py-2 max-h-[40px] flex items-center justify-between gap-3 text-xs w-full">
771
  <div className="flex items-center gap-2 flex-1">
772
+ <TriangleAlert className="size-4 text-neutral-500 flex-shrink-0" />
773
+ <span className="text-neutral-400 font-medium">
774
  Preview version of the project. Try refreshing the preview if
775
  you experience any issues.
776
  </span>
777
  </div>
778
  <button
779
  onClick={refreshIframe}
780
+ className="cursor-pointer text-xs px-3 py-1 bg-neutral-800 hover:bg-neutral-700 text-neutral-300 rounded-md font-medium transition-colors whitespace-nowrap flex items-center gap-1.5"
781
  >
782
+ <RefreshCcw className="size-3 text-neutral-300 flex-shrink-0" />
783
  Refresh
784
  </button>
785
  </div>
components/my-projects/project-card.tsx CHANGED
@@ -1,6 +1,12 @@
1
  import Link from "next/link";
2
  import { formatDistance } from "date-fns";
3
- import { EllipsisVertical, Lock, Settings, Trash } from "lucide-react";
 
 
 
 
 
 
4
 
5
  import { Button } from "@/components/ui/button";
6
  import {
@@ -37,6 +43,10 @@ export function ProjectCard({
37
  onDelete();
38
  }
39
  };
 
 
 
 
40
  // from-gray-600 to-gray-600
41
  // from-blue-600 to-blue-600
42
  // from-green-600 to-green-600
@@ -120,6 +130,10 @@ export function ProjectCard({
120
  Project Settings
121
  </DropdownMenuItem>
122
  </a>
 
 
 
 
123
  <DropdownMenuItem variant="destructive" onClick={handleDelete}>
124
  <Trash className="size-4 text-red-500" />
125
  Delete Project
 
1
  import Link from "next/link";
2
  import { formatDistance } from "date-fns";
3
+ import {
4
+ Download,
5
+ EllipsisVertical,
6
+ Lock,
7
+ Settings,
8
+ Trash,
9
+ } from "lucide-react";
10
 
11
  import { Button } from "@/components/ui/button";
12
  import {
 
43
  onDelete();
44
  }
45
  };
46
+
47
+ const handleDownload = () => {
48
+ window.open(`/deepsite/api/me/projects/${project.name}/download`, "_blank");
49
+ };
50
  // from-gray-600 to-gray-600
51
  // from-blue-600 to-blue-600
52
  // from-green-600 to-green-600
 
130
  Project Settings
131
  </DropdownMenuItem>
132
  </a>
133
+ <DropdownMenuItem onClick={handleDownload}>
134
+ <Download className="size-4 text-neutral-100" />
135
+ Download as ZIP
136
+ </DropdownMenuItem>
137
  <DropdownMenuItem variant="destructive" onClick={handleDelete}>
138
  <Trash className="size-4 text-red-500" />
139
  Delete Project
components/public/navigation/index.tsx CHANGED
@@ -148,7 +148,7 @@ export default function Navigation() {
148
  <Link href="https://discord.gg/KpanwM3vXa" target="_blank">
149
  <Button
150
  variant="bordered"
151
- className="!border-indigo-500 !text-white !bg-indigo-500 shadow-lg shadow-indigo-500/50 hover:shadow-indigo-500/70 transition-all duration-300"
152
  >
153
  <DiscordIcon className="size-4 mr-0.5" />
154
  <span className="max-lg:hidden">Discord Community</span>
 
148
  <Link href="https://discord.gg/KpanwM3vXa" target="_blank">
149
  <Button
150
  variant="bordered"
151
+ className="!border-indigo-500 !text-white !bg-indigo-500 transition-all duration-300"
152
  >
153
  <DiscordIcon className="size-4 mr-0.5" />
154
  <span className="max-lg:hidden">Discord Community</span>
package-lock.json CHANGED
@@ -33,6 +33,7 @@
33
  "clsx": "^2.1.1",
34
  "date-fns": "^4.1.0",
35
  "framer-motion": "^12.23.22",
 
36
  "log4js": "^6.9.1",
37
  "log4js-json-layout": "^2.2.3",
38
  "lucide-react": "^0.542.0",
@@ -3239,6 +3240,12 @@
3239
  "toggle-selection": "^1.0.6"
3240
  }
3241
  },
 
 
 
 
 
 
3242
  "node_modules/cross-spawn": {
3243
  "version": "7.0.6",
3244
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -4085,6 +4092,12 @@
4085
  "node": ">= 4"
4086
  }
4087
  },
 
 
 
 
 
 
4088
  "node_modules/import-fresh": {
4089
  "version": "3.3.1",
4090
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -4112,6 +4125,12 @@
4112
  "node": ">=0.8.19"
4113
  }
4114
  },
 
 
 
 
 
 
4115
  "node_modules/inline-style-prefixer": {
4116
  "version": "7.0.1",
4117
  "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
@@ -4158,6 +4177,12 @@
4158
  "node": ">=0.12.0"
4159
  }
4160
  },
 
 
 
 
 
 
4161
  "node_modules/isexe": {
4162
  "version": "2.0.0",
4163
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -4273,6 +4298,18 @@
4273
  "graceful-fs": "^4.1.6"
4274
  }
4275
  },
 
 
 
 
 
 
 
 
 
 
 
 
4276
  "node_modules/kareem": {
4277
  "version": "2.6.3",
4278
  "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
@@ -4306,6 +4343,15 @@
4306
  "node": ">= 0.8.0"
4307
  }
4308
  },
 
 
 
 
 
 
 
 
 
4309
  "node_modules/lightningcss": {
4310
  "version": "1.30.1",
4311
  "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
@@ -5086,6 +5132,12 @@
5086
  "url": "https://github.com/sponsors/sindresorhus"
5087
  }
5088
  },
 
 
 
 
 
 
5089
  "node_modules/parent-module": {
5090
  "version": "1.0.1",
5091
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -5176,6 +5228,12 @@
5176
  "node": ">= 0.8.0"
5177
  }
5178
  },
 
 
 
 
 
 
5179
  "node_modules/proxy-from-env": {
5180
  "version": "1.1.0",
5181
  "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -5346,6 +5404,27 @@
5346
  "react-dom": "*"
5347
  }
5348
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5349
  "node_modules/require-from-string": {
5350
  "version": "2.0.2",
5351
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -5508,6 +5587,12 @@
5508
  "node": ">=6.9"
5509
  }
5510
  },
 
 
 
 
 
 
5511
  "node_modules/sharp": {
5512
  "version": "0.34.3",
5513
  "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
@@ -5703,6 +5788,21 @@
5703
  "node": ">=8.0"
5704
  }
5705
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5706
  "node_modules/strip-json-comments": {
5707
  "version": "3.1.1",
5708
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -6157,6 +6257,12 @@
6157
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
6158
  }
6159
  },
 
 
 
 
 
 
6160
  "node_modules/watchpack": {
6161
  "version": "2.4.4",
6162
  "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
 
33
  "clsx": "^2.1.1",
34
  "date-fns": "^4.1.0",
35
  "framer-motion": "^12.23.22",
36
+ "jszip": "^3.10.1",
37
  "log4js": "^6.9.1",
38
  "log4js-json-layout": "^2.2.3",
39
  "lucide-react": "^0.542.0",
 
3240
  "toggle-selection": "^1.0.6"
3241
  }
3242
  },
3243
+ "node_modules/core-util-is": {
3244
+ "version": "1.0.3",
3245
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
3246
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
3247
+ "license": "MIT"
3248
+ },
3249
  "node_modules/cross-spawn": {
3250
  "version": "7.0.6",
3251
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
 
4092
  "node": ">= 4"
4093
  }
4094
  },
4095
+ "node_modules/immediate": {
4096
+ "version": "3.0.6",
4097
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
4098
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
4099
+ "license": "MIT"
4100
+ },
4101
  "node_modules/import-fresh": {
4102
  "version": "3.3.1",
4103
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
 
4125
  "node": ">=0.8.19"
4126
  }
4127
  },
4128
+ "node_modules/inherits": {
4129
+ "version": "2.0.4",
4130
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
4131
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
4132
+ "license": "ISC"
4133
+ },
4134
  "node_modules/inline-style-prefixer": {
4135
  "version": "7.0.1",
4136
  "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
 
4177
  "node": ">=0.12.0"
4178
  }
4179
  },
4180
+ "node_modules/isarray": {
4181
+ "version": "1.0.0",
4182
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
4183
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
4184
+ "license": "MIT"
4185
+ },
4186
  "node_modules/isexe": {
4187
  "version": "2.0.0",
4188
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
 
4298
  "graceful-fs": "^4.1.6"
4299
  }
4300
  },
4301
+ "node_modules/jszip": {
4302
+ "version": "3.10.1",
4303
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
4304
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
4305
+ "license": "(MIT OR GPL-3.0-or-later)",
4306
+ "dependencies": {
4307
+ "lie": "~3.3.0",
4308
+ "pako": "~1.0.2",
4309
+ "readable-stream": "~2.3.6",
4310
+ "setimmediate": "^1.0.5"
4311
+ }
4312
+ },
4313
  "node_modules/kareem": {
4314
  "version": "2.6.3",
4315
  "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
 
4343
  "node": ">= 0.8.0"
4344
  }
4345
  },
4346
+ "node_modules/lie": {
4347
+ "version": "3.3.0",
4348
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
4349
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
4350
+ "license": "MIT",
4351
+ "dependencies": {
4352
+ "immediate": "~3.0.5"
4353
+ }
4354
+ },
4355
  "node_modules/lightningcss": {
4356
  "version": "1.30.1",
4357
  "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
 
5132
  "url": "https://github.com/sponsors/sindresorhus"
5133
  }
5134
  },
5135
+ "node_modules/pako": {
5136
+ "version": "1.0.11",
5137
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
5138
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
5139
+ "license": "(MIT AND Zlib)"
5140
+ },
5141
  "node_modules/parent-module": {
5142
  "version": "1.0.1",
5143
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
 
5228
  "node": ">= 0.8.0"
5229
  }
5230
  },
5231
+ "node_modules/process-nextick-args": {
5232
+ "version": "2.0.1",
5233
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
5234
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
5235
+ "license": "MIT"
5236
+ },
5237
  "node_modules/proxy-from-env": {
5238
  "version": "1.1.0",
5239
  "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
 
5404
  "react-dom": "*"
5405
  }
5406
  },
5407
+ "node_modules/readable-stream": {
5408
+ "version": "2.3.8",
5409
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
5410
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
5411
+ "license": "MIT",
5412
+ "dependencies": {
5413
+ "core-util-is": "~1.0.0",
5414
+ "inherits": "~2.0.3",
5415
+ "isarray": "~1.0.0",
5416
+ "process-nextick-args": "~2.0.0",
5417
+ "safe-buffer": "~5.1.1",
5418
+ "string_decoder": "~1.1.1",
5419
+ "util-deprecate": "~1.0.1"
5420
+ }
5421
+ },
5422
+ "node_modules/readable-stream/node_modules/safe-buffer": {
5423
+ "version": "5.1.2",
5424
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
5425
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
5426
+ "license": "MIT"
5427
+ },
5428
  "node_modules/require-from-string": {
5429
  "version": "2.0.2",
5430
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
 
5587
  "node": ">=6.9"
5588
  }
5589
  },
5590
+ "node_modules/setimmediate": {
5591
+ "version": "1.0.5",
5592
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
5593
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
5594
+ "license": "MIT"
5595
+ },
5596
  "node_modules/sharp": {
5597
  "version": "0.34.3",
5598
  "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
 
5788
  "node": ">=8.0"
5789
  }
5790
  },
5791
+ "node_modules/string_decoder": {
5792
+ "version": "1.1.1",
5793
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
5794
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
5795
+ "license": "MIT",
5796
+ "dependencies": {
5797
+ "safe-buffer": "~5.1.0"
5798
+ }
5799
+ },
5800
+ "node_modules/string_decoder/node_modules/safe-buffer": {
5801
+ "version": "5.1.2",
5802
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
5803
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
5804
+ "license": "MIT"
5805
+ },
5806
  "node_modules/strip-json-comments": {
5807
  "version": "3.1.1",
5808
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
 
6257
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
6258
  }
6259
  },
6260
+ "node_modules/util-deprecate": {
6261
+ "version": "1.0.2",
6262
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
6263
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
6264
+ "license": "MIT"
6265
+ },
6266
  "node_modules/watchpack": {
6267
  "version": "2.4.4",
6268
  "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
package.json CHANGED
@@ -33,6 +33,7 @@
33
  "clsx": "^2.1.1",
34
  "date-fns": "^4.1.0",
35
  "framer-motion": "^12.23.22",
 
36
  "log4js": "^6.9.1",
37
  "log4js-json-layout": "^2.2.3",
38
  "lucide-react": "^0.542.0",
 
33
  "clsx": "^2.1.1",
34
  "date-fns": "^4.1.0",
35
  "framer-motion": "^12.23.22",
36
+ "jszip": "^3.10.1",
37
  "log4js": "^6.9.1",
38
  "log4js-json-layout": "^2.2.3",
39
  "lucide-react": "^0.542.0",