Files changed (49) hide show
  1. .gitattributes +0 -1
  2. README.md +1 -4
  3. components.json +0 -23
  4. index.html +0 -23
  5. package-lock.json +23 -1181
  6. package.json +1 -20
  7. public/banner.png +0 -3
  8. public/providers/together.svg +0 -10
  9. server.js +56 -346
  10. src/assets/index.css +0 -133
  11. src/assets/linux.png +0 -0
  12. src/{views → components}/App.tsx +136 -185
  13. src/components/ask-ai/ask-ai.tsx +119 -264
  14. src/components/deploy-button/deploy-button.tsx +156 -91
  15. src/components/footer/footer.tsx +0 -177
  16. src/components/header/header.tsx +19 -48
  17. src/components/history/history.tsx +0 -138
  18. src/components/invite-friends/invite-friends.tsx +0 -78
  19. src/components/load-button/load-button.tsx +51 -43
  20. src/components/loading/loading.tsx +3 -16
  21. src/components/login/login.tsx +4 -4
  22. src/components/magicui/grid-pattern.tsx +0 -69
  23. src/components/preview/preview.tsx +52 -46
  24. src/components/pro-modal/pro-modal.tsx +40 -64
  25. src/components/re-imagine/re-imagine.tsx +0 -136
  26. src/components/settings/settings.tsx +95 -154
  27. src/components/tabs/tabs.tsx +120 -0
  28. src/components/theme/mode-toggle.tsx +0 -37
  29. src/components/theme/theme-provider.tsx +0 -75
  30. src/components/ui/avatar.tsx +0 -51
  31. src/components/ui/button.tsx +0 -64
  32. src/components/ui/dialog.tsx +0 -144
  33. src/components/ui/dropdown-menu.tsx +0 -258
  34. src/components/ui/input.tsx +0 -21
  35. src/components/ui/popover.tsx +0 -46
  36. src/components/ui/select.tsx +0 -189
  37. src/components/ui/sonner.tsx +0 -23
  38. src/components/ui/tabs.tsx +0 -64
  39. src/components/ui/toggle-group.tsx +0 -71
  40. src/components/ui/toggle.tsx +0 -47
  41. src/components/ui/tooltip.tsx +0 -59
  42. src/lib/utils.ts +0 -6
  43. src/main.tsx +3 -1
  44. tsconfig.app.json +0 -1
  45. tsconfig.json +3 -7
  46. utils/consts.ts +31 -10
  47. utils/providers.js +6 -35
  48. utils/types.ts +0 -6
  49. vite.config.ts +3 -6
.gitattributes DELETED
@@ -1 +0,0 @@
1
- public/banner.png filter=lfs diff=lfs merge=lfs -text
 
 
README.md CHANGED
@@ -10,13 +10,10 @@ license: mit
10
  short_description: Generate any application with DeepSeek
11
  models:
12
  - deepseek-ai/DeepSeek-V3-0324
13
- - deepseek-ai/DeepSeek-R1-0528
14
  ---
15
 
16
  # DeepSite 🐳
17
-
18
  DeepSite is a coding platform powered by DeepSeek AI, designed to make coding smarter and more efficient. Tailored for developers, data scientists, and AI engineers, it integrates generative AI into your coding projects to enhance creativity and productivity.
19
 
20
  ## How to use it locally
21
-
22
- Follow [this discussion](https://huggingface.co/spaces/enzostvs/deepsite/discussions/74)
 
10
  short_description: Generate any application with DeepSeek
11
  models:
12
  - deepseek-ai/DeepSeek-V3-0324
 
13
  ---
14
 
15
  # DeepSite 🐳
 
16
  DeepSite is a coding platform powered by DeepSeek AI, designed to make coding smarter and more efficient. Tailored for developers, data scientists, and AI engineers, it integrates generative AI into your coding projects to enhance creativity and productivity.
17
 
18
  ## How to use it locally
19
+ Follow [this discussion](https://huggingface.co/spaces/enzostvs/deepsite/discussions/74)
 
components.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
4
- "rsc": false,
5
- "tsx": true,
6
- "tailwind": {
7
- "config": "",
8
- "css": "src/assets/index.css",
9
- "baseColor": "neutral",
10
- "baseColorLight": "slate",
11
- "baseColorDark": "neutral",
12
- "cssVariables": false,
13
- "prefix": ""
14
- },
15
- "aliases": {
16
- "components": "@/components",
17
- "utils": "@/lib/utils",
18
- "ui": "@/components/ui",
19
- "lib": "@/lib",
20
- "hooks": "@/hooks"
21
- },
22
- "iconLibrary": "lucide"
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
index.html CHANGED
@@ -11,29 +11,6 @@
11
  helps you build websites with AI, no code required. Let's deploy your
12
  website with DeepSite and enjoy the magic of AI."
13
  />
14
- <meta name="theme-color" content="#000000" />
15
- <meta name="apple-mobile-web-app-capable" content="yes" />
16
- <meta name="apple-mobile-web-app-status-bar-style" content="black" />
17
- <meta name="apple-mobile-web-app-title" content="DeepSite" />
18
- <meta name="msapplication-TileColor" content="#000000" />
19
- <meta name="twitter:card" content="summary_large_image" />
20
- <meta name="twitter:title" content="DeepSite | Build with AI ✨" />
21
- <meta
22
- name="twitter:description"
23
- content="DeepSite is a web development tool that
24
- helps you build websites with AI, no code required. Let's deploy your
25
- website with DeepSite and enjoy the magic of AI."
26
- />
27
- <meta name="twitter:image" content="/banner.png" />
28
- <meta property="og:type" content="website" />
29
- <meta property="og:title" content="DeepSite | Build with AI ✨" />
30
- <meta
31
- property="og:description"
32
- content="DeepSite is a web development tool that
33
- helps you build websites with AI, no code required. Let's deploy your
34
- website with DeepSite and enjoy the magic of AI."
35
- />
36
- <meta property="og:image" content="/banner.png" />
37
  <link rel="preconnect" href="https://fonts.googleapis.com" />
38
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
39
  <link
 
11
  helps you build websites with AI, no code required. Let's deploy your
12
  website with DeepSite and enjoy the magic of AI."
13
  />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  <link rel="preconnect" href="https://fonts.googleapis.com" />
15
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
16
  <link
package-lock.json CHANGED
@@ -9,30 +9,15 @@
9
  "version": "0.0.0",
10
  "dependencies": {
11
  "@huggingface/hub": "^1.1.1",
12
- "@huggingface/inference": "^4.0.2",
13
  "@monaco-editor/react": "^4.7.0",
14
- "@radix-ui/react-avatar": "^1.1.10",
15
- "@radix-ui/react-dialog": "^1.1.14",
16
- "@radix-ui/react-dropdown-menu": "^2.1.15",
17
- "@radix-ui/react-popover": "^1.1.14",
18
- "@radix-ui/react-select": "^2.2.5",
19
- "@radix-ui/react-slot": "^1.2.3",
20
- "@radix-ui/react-switch": "^1.2.5",
21
- "@radix-ui/react-tabs": "^1.1.12",
22
- "@radix-ui/react-toggle": "^1.1.9",
23
- "@radix-ui/react-toggle-group": "^1.1.10",
24
- "@radix-ui/react-tooltip": "^1.2.7",
25
  "@tailwindcss/vite": "^4.0.15",
26
  "@xenova/transformers": "^2.17.2",
27
  "body-parser": "^1.20.3",
28
- "class-variance-authority": "^0.7.1",
29
  "classnames": "^2.5.1",
30
- "clsx": "^2.1.1",
31
  "cookie-parser": "^1.4.7",
32
  "dotenv": "^16.4.7",
33
  "express": "^4.21.2",
34
- "lucide-react": "^0.511.0",
35
- "next-themes": "^0.4.6",
36
  "react": "^19.0.0",
37
  "react-dom": "^19.0.0",
38
  "react-icons": "^5.5.0",
@@ -40,14 +25,11 @@
40
  "react-speech-recognition": "^4.0.0",
41
  "react-toastify": "^11.0.5",
42
  "react-use": "^17.6.0",
43
- "sonner": "^2.0.3",
44
- "tailwind-merge": "^3.3.0",
45
  "tailwindcss": "^4.0.15"
46
  },
47
  "devDependencies": {
48
  "@eslint/js": "^9.21.0",
49
  "@types/express": "^5.0.1",
50
- "@types/node": "^22.15.21",
51
  "@types/react": "^19.0.10",
52
  "@types/react-dom": "^19.0.4",
53
  "@types/react-speech-recognition": "^3.9.6",
@@ -56,7 +38,6 @@
56
  "eslint-plugin-react-hooks": "^5.1.0",
57
  "eslint-plugin-react-refresh": "^0.4.19",
58
  "globals": "^15.15.0",
59
- "tw-animate-css": "^1.3.0",
60
  "typescript": "~5.7.2",
61
  "typescript-eslint": "^8.24.1",
62
  "vite": "^6.2.0"
@@ -191,11 +172,10 @@
191
  }
192
  },
193
  "node_modules/@babel/helper-plugin-utils": {
194
- "version": "7.27.1",
195
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
196
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
197
  "dev": true,
198
- "license": "MIT",
199
  "engines": {
200
  "node": ">=6.9.0"
201
  }
@@ -865,44 +845,6 @@
865
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
866
  }
867
  },
868
- "node_modules/@floating-ui/core": {
869
- "version": "1.7.0",
870
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz",
871
- "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==",
872
- "license": "MIT",
873
- "dependencies": {
874
- "@floating-ui/utils": "^0.2.9"
875
- }
876
- },
877
- "node_modules/@floating-ui/dom": {
878
- "version": "1.7.0",
879
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz",
880
- "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==",
881
- "license": "MIT",
882
- "dependencies": {
883
- "@floating-ui/core": "^1.7.0",
884
- "@floating-ui/utils": "^0.2.9"
885
- }
886
- },
887
- "node_modules/@floating-ui/react-dom": {
888
- "version": "2.1.2",
889
- "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
890
- "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
891
- "license": "MIT",
892
- "dependencies": {
893
- "@floating-ui/dom": "^1.0.0"
894
- },
895
- "peerDependencies": {
896
- "react": ">=16.8.0",
897
- "react-dom": ">=16.8.0"
898
- }
899
- },
900
- "node_modules/@floating-ui/utils": {
901
- "version": "0.2.9",
902
- "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
903
- "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
904
- "license": "MIT"
905
- },
906
  "node_modules/@huggingface/hub": {
907
  "version": "1.1.1",
908
  "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-1.1.1.tgz",
@@ -920,32 +862,29 @@
920
  "integrity": "sha512-HK6JTVB/nrgjOnbe77HFSENftfAp67AI4mHMR2x64Os1hvchuTT88M8fKEiyESSvqKFKwW4lQKkHva07p05AXw=="
921
  },
922
  "node_modules/@huggingface/inference": {
923
- "version": "4.0.2",
924
- "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-4.0.2.tgz",
925
- "integrity": "sha512-XuWb8ocH7lA5kSdXrGnqshtRz3ocSBzEzxcp5xeAXLjgM1ocoIHq+RW8/Ti0xq3MeRGQWgUkYPCgDV/xgs8p4g==",
926
- "license": "MIT",
927
  "dependencies": {
928
- "@huggingface/jinja": "^0.5.0",
929
- "@huggingface/tasks": "^0.19.11"
930
  },
931
  "engines": {
932
  "node": ">=18"
933
  }
934
  },
935
  "node_modules/@huggingface/jinja": {
936
- "version": "0.5.0",
937
- "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.0.tgz",
938
- "integrity": "sha512-Ptc03/jGRiYRoi0bUYKZ14MkDslsBRT24oxmsvUlfYrvQMldrxCevhPnT+hfX8awKTT8/f/0ZBBWldoeAcMHdQ==",
939
- "license": "MIT",
940
  "engines": {
941
  "node": ">=18"
942
  }
943
  },
944
  "node_modules/@huggingface/tasks": {
945
- "version": "0.19.11",
946
- "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.19.11.tgz",
947
- "integrity": "sha512-oBhSgVlg7Pp643MsH8BiI3OAXIMJNxdSiMtv4mApRZV8dmAz8oasKhg6CVKIplO7vAO7F6dkmMn4bYM64I2A9w==",
948
- "license": "MIT"
949
  },
950
  "node_modules/@humanfs/core": {
951
  "version": "0.19.1",
@@ -1165,891 +1104,6 @@
1165
  "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
1166
  "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
1167
  },
1168
- "node_modules/@radix-ui/number": {
1169
- "version": "1.1.1",
1170
- "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
1171
- "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
1172
- "license": "MIT"
1173
- },
1174
- "node_modules/@radix-ui/primitive": {
1175
- "version": "1.1.2",
1176
- "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
1177
- "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
1178
- "license": "MIT"
1179
- },
1180
- "node_modules/@radix-ui/react-arrow": {
1181
- "version": "1.1.7",
1182
- "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
1183
- "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
1184
- "license": "MIT",
1185
- "dependencies": {
1186
- "@radix-ui/react-primitive": "2.1.3"
1187
- },
1188
- "peerDependencies": {
1189
- "@types/react": "*",
1190
- "@types/react-dom": "*",
1191
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1192
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1193
- },
1194
- "peerDependenciesMeta": {
1195
- "@types/react": {
1196
- "optional": true
1197
- },
1198
- "@types/react-dom": {
1199
- "optional": true
1200
- }
1201
- }
1202
- },
1203
- "node_modules/@radix-ui/react-avatar": {
1204
- "version": "1.1.10",
1205
- "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz",
1206
- "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==",
1207
- "license": "MIT",
1208
- "dependencies": {
1209
- "@radix-ui/react-context": "1.1.2",
1210
- "@radix-ui/react-primitive": "2.1.3",
1211
- "@radix-ui/react-use-callback-ref": "1.1.1",
1212
- "@radix-ui/react-use-is-hydrated": "0.1.0",
1213
- "@radix-ui/react-use-layout-effect": "1.1.1"
1214
- },
1215
- "peerDependencies": {
1216
- "@types/react": "*",
1217
- "@types/react-dom": "*",
1218
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1219
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1220
- },
1221
- "peerDependenciesMeta": {
1222
- "@types/react": {
1223
- "optional": true
1224
- },
1225
- "@types/react-dom": {
1226
- "optional": true
1227
- }
1228
- }
1229
- },
1230
- "node_modules/@radix-ui/react-collection": {
1231
- "version": "1.1.7",
1232
- "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
1233
- "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
1234
- "license": "MIT",
1235
- "dependencies": {
1236
- "@radix-ui/react-compose-refs": "1.1.2",
1237
- "@radix-ui/react-context": "1.1.2",
1238
- "@radix-ui/react-primitive": "2.1.3",
1239
- "@radix-ui/react-slot": "1.2.3"
1240
- },
1241
- "peerDependencies": {
1242
- "@types/react": "*",
1243
- "@types/react-dom": "*",
1244
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1245
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1246
- },
1247
- "peerDependenciesMeta": {
1248
- "@types/react": {
1249
- "optional": true
1250
- },
1251
- "@types/react-dom": {
1252
- "optional": true
1253
- }
1254
- }
1255
- },
1256
- "node_modules/@radix-ui/react-compose-refs": {
1257
- "version": "1.1.2",
1258
- "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
1259
- "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
1260
- "license": "MIT",
1261
- "peerDependencies": {
1262
- "@types/react": "*",
1263
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1264
- },
1265
- "peerDependenciesMeta": {
1266
- "@types/react": {
1267
- "optional": true
1268
- }
1269
- }
1270
- },
1271
- "node_modules/@radix-ui/react-context": {
1272
- "version": "1.1.2",
1273
- "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
1274
- "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
1275
- "license": "MIT",
1276
- "peerDependencies": {
1277
- "@types/react": "*",
1278
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1279
- },
1280
- "peerDependenciesMeta": {
1281
- "@types/react": {
1282
- "optional": true
1283
- }
1284
- }
1285
- },
1286
- "node_modules/@radix-ui/react-dialog": {
1287
- "version": "1.1.14",
1288
- "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz",
1289
- "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==",
1290
- "license": "MIT",
1291
- "dependencies": {
1292
- "@radix-ui/primitive": "1.1.2",
1293
- "@radix-ui/react-compose-refs": "1.1.2",
1294
- "@radix-ui/react-context": "1.1.2",
1295
- "@radix-ui/react-dismissable-layer": "1.1.10",
1296
- "@radix-ui/react-focus-guards": "1.1.2",
1297
- "@radix-ui/react-focus-scope": "1.1.7",
1298
- "@radix-ui/react-id": "1.1.1",
1299
- "@radix-ui/react-portal": "1.1.9",
1300
- "@radix-ui/react-presence": "1.1.4",
1301
- "@radix-ui/react-primitive": "2.1.3",
1302
- "@radix-ui/react-slot": "1.2.3",
1303
- "@radix-ui/react-use-controllable-state": "1.2.2",
1304
- "aria-hidden": "^1.2.4",
1305
- "react-remove-scroll": "^2.6.3"
1306
- },
1307
- "peerDependencies": {
1308
- "@types/react": "*",
1309
- "@types/react-dom": "*",
1310
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1311
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1312
- },
1313
- "peerDependenciesMeta": {
1314
- "@types/react": {
1315
- "optional": true
1316
- },
1317
- "@types/react-dom": {
1318
- "optional": true
1319
- }
1320
- }
1321
- },
1322
- "node_modules/@radix-ui/react-direction": {
1323
- "version": "1.1.1",
1324
- "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
1325
- "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
1326
- "license": "MIT",
1327
- "peerDependencies": {
1328
- "@types/react": "*",
1329
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1330
- },
1331
- "peerDependenciesMeta": {
1332
- "@types/react": {
1333
- "optional": true
1334
- }
1335
- }
1336
- },
1337
- "node_modules/@radix-ui/react-dismissable-layer": {
1338
- "version": "1.1.10",
1339
- "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
1340
- "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==",
1341
- "license": "MIT",
1342
- "dependencies": {
1343
- "@radix-ui/primitive": "1.1.2",
1344
- "@radix-ui/react-compose-refs": "1.1.2",
1345
- "@radix-ui/react-primitive": "2.1.3",
1346
- "@radix-ui/react-use-callback-ref": "1.1.1",
1347
- "@radix-ui/react-use-escape-keydown": "1.1.1"
1348
- },
1349
- "peerDependencies": {
1350
- "@types/react": "*",
1351
- "@types/react-dom": "*",
1352
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1353
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1354
- },
1355
- "peerDependenciesMeta": {
1356
- "@types/react": {
1357
- "optional": true
1358
- },
1359
- "@types/react-dom": {
1360
- "optional": true
1361
- }
1362
- }
1363
- },
1364
- "node_modules/@radix-ui/react-dropdown-menu": {
1365
- "version": "2.1.15",
1366
- "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz",
1367
- "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==",
1368
- "license": "MIT",
1369
- "dependencies": {
1370
- "@radix-ui/primitive": "1.1.2",
1371
- "@radix-ui/react-compose-refs": "1.1.2",
1372
- "@radix-ui/react-context": "1.1.2",
1373
- "@radix-ui/react-id": "1.1.1",
1374
- "@radix-ui/react-menu": "2.1.15",
1375
- "@radix-ui/react-primitive": "2.1.3",
1376
- "@radix-ui/react-use-controllable-state": "1.2.2"
1377
- },
1378
- "peerDependencies": {
1379
- "@types/react": "*",
1380
- "@types/react-dom": "*",
1381
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1382
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1383
- },
1384
- "peerDependenciesMeta": {
1385
- "@types/react": {
1386
- "optional": true
1387
- },
1388
- "@types/react-dom": {
1389
- "optional": true
1390
- }
1391
- }
1392
- },
1393
- "node_modules/@radix-ui/react-focus-guards": {
1394
- "version": "1.1.2",
1395
- "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
1396
- "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==",
1397
- "license": "MIT",
1398
- "peerDependencies": {
1399
- "@types/react": "*",
1400
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1401
- },
1402
- "peerDependenciesMeta": {
1403
- "@types/react": {
1404
- "optional": true
1405
- }
1406
- }
1407
- },
1408
- "node_modules/@radix-ui/react-focus-scope": {
1409
- "version": "1.1.7",
1410
- "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
1411
- "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
1412
- "license": "MIT",
1413
- "dependencies": {
1414
- "@radix-ui/react-compose-refs": "1.1.2",
1415
- "@radix-ui/react-primitive": "2.1.3",
1416
- "@radix-ui/react-use-callback-ref": "1.1.1"
1417
- },
1418
- "peerDependencies": {
1419
- "@types/react": "*",
1420
- "@types/react-dom": "*",
1421
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1422
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1423
- },
1424
- "peerDependenciesMeta": {
1425
- "@types/react": {
1426
- "optional": true
1427
- },
1428
- "@types/react-dom": {
1429
- "optional": true
1430
- }
1431
- }
1432
- },
1433
- "node_modules/@radix-ui/react-id": {
1434
- "version": "1.1.1",
1435
- "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
1436
- "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
1437
- "license": "MIT",
1438
- "dependencies": {
1439
- "@radix-ui/react-use-layout-effect": "1.1.1"
1440
- },
1441
- "peerDependencies": {
1442
- "@types/react": "*",
1443
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1444
- },
1445
- "peerDependenciesMeta": {
1446
- "@types/react": {
1447
- "optional": true
1448
- }
1449
- }
1450
- },
1451
- "node_modules/@radix-ui/react-menu": {
1452
- "version": "2.1.15",
1453
- "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz",
1454
- "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==",
1455
- "license": "MIT",
1456
- "dependencies": {
1457
- "@radix-ui/primitive": "1.1.2",
1458
- "@radix-ui/react-collection": "1.1.7",
1459
- "@radix-ui/react-compose-refs": "1.1.2",
1460
- "@radix-ui/react-context": "1.1.2",
1461
- "@radix-ui/react-direction": "1.1.1",
1462
- "@radix-ui/react-dismissable-layer": "1.1.10",
1463
- "@radix-ui/react-focus-guards": "1.1.2",
1464
- "@radix-ui/react-focus-scope": "1.1.7",
1465
- "@radix-ui/react-id": "1.1.1",
1466
- "@radix-ui/react-popper": "1.2.7",
1467
- "@radix-ui/react-portal": "1.1.9",
1468
- "@radix-ui/react-presence": "1.1.4",
1469
- "@radix-ui/react-primitive": "2.1.3",
1470
- "@radix-ui/react-roving-focus": "1.1.10",
1471
- "@radix-ui/react-slot": "1.2.3",
1472
- "@radix-ui/react-use-callback-ref": "1.1.1",
1473
- "aria-hidden": "^1.2.4",
1474
- "react-remove-scroll": "^2.6.3"
1475
- },
1476
- "peerDependencies": {
1477
- "@types/react": "*",
1478
- "@types/react-dom": "*",
1479
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1480
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1481
- },
1482
- "peerDependenciesMeta": {
1483
- "@types/react": {
1484
- "optional": true
1485
- },
1486
- "@types/react-dom": {
1487
- "optional": true
1488
- }
1489
- }
1490
- },
1491
- "node_modules/@radix-ui/react-popover": {
1492
- "version": "1.1.14",
1493
- "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz",
1494
- "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==",
1495
- "license": "MIT",
1496
- "dependencies": {
1497
- "@radix-ui/primitive": "1.1.2",
1498
- "@radix-ui/react-compose-refs": "1.1.2",
1499
- "@radix-ui/react-context": "1.1.2",
1500
- "@radix-ui/react-dismissable-layer": "1.1.10",
1501
- "@radix-ui/react-focus-guards": "1.1.2",
1502
- "@radix-ui/react-focus-scope": "1.1.7",
1503
- "@radix-ui/react-id": "1.1.1",
1504
- "@radix-ui/react-popper": "1.2.7",
1505
- "@radix-ui/react-portal": "1.1.9",
1506
- "@radix-ui/react-presence": "1.1.4",
1507
- "@radix-ui/react-primitive": "2.1.3",
1508
- "@radix-ui/react-slot": "1.2.3",
1509
- "@radix-ui/react-use-controllable-state": "1.2.2",
1510
- "aria-hidden": "^1.2.4",
1511
- "react-remove-scroll": "^2.6.3"
1512
- },
1513
- "peerDependencies": {
1514
- "@types/react": "*",
1515
- "@types/react-dom": "*",
1516
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1517
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1518
- },
1519
- "peerDependenciesMeta": {
1520
- "@types/react": {
1521
- "optional": true
1522
- },
1523
- "@types/react-dom": {
1524
- "optional": true
1525
- }
1526
- }
1527
- },
1528
- "node_modules/@radix-ui/react-popper": {
1529
- "version": "1.2.7",
1530
- "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
1531
- "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
1532
- "license": "MIT",
1533
- "dependencies": {
1534
- "@floating-ui/react-dom": "^2.0.0",
1535
- "@radix-ui/react-arrow": "1.1.7",
1536
- "@radix-ui/react-compose-refs": "1.1.2",
1537
- "@radix-ui/react-context": "1.1.2",
1538
- "@radix-ui/react-primitive": "2.1.3",
1539
- "@radix-ui/react-use-callback-ref": "1.1.1",
1540
- "@radix-ui/react-use-layout-effect": "1.1.1",
1541
- "@radix-ui/react-use-rect": "1.1.1",
1542
- "@radix-ui/react-use-size": "1.1.1",
1543
- "@radix-ui/rect": "1.1.1"
1544
- },
1545
- "peerDependencies": {
1546
- "@types/react": "*",
1547
- "@types/react-dom": "*",
1548
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1549
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1550
- },
1551
- "peerDependenciesMeta": {
1552
- "@types/react": {
1553
- "optional": true
1554
- },
1555
- "@types/react-dom": {
1556
- "optional": true
1557
- }
1558
- }
1559
- },
1560
- "node_modules/@radix-ui/react-portal": {
1561
- "version": "1.1.9",
1562
- "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
1563
- "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
1564
- "license": "MIT",
1565
- "dependencies": {
1566
- "@radix-ui/react-primitive": "2.1.3",
1567
- "@radix-ui/react-use-layout-effect": "1.1.1"
1568
- },
1569
- "peerDependencies": {
1570
- "@types/react": "*",
1571
- "@types/react-dom": "*",
1572
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1573
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1574
- },
1575
- "peerDependenciesMeta": {
1576
- "@types/react": {
1577
- "optional": true
1578
- },
1579
- "@types/react-dom": {
1580
- "optional": true
1581
- }
1582
- }
1583
- },
1584
- "node_modules/@radix-ui/react-presence": {
1585
- "version": "1.1.4",
1586
- "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
1587
- "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
1588
- "license": "MIT",
1589
- "dependencies": {
1590
- "@radix-ui/react-compose-refs": "1.1.2",
1591
- "@radix-ui/react-use-layout-effect": "1.1.1"
1592
- },
1593
- "peerDependencies": {
1594
- "@types/react": "*",
1595
- "@types/react-dom": "*",
1596
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1597
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1598
- },
1599
- "peerDependenciesMeta": {
1600
- "@types/react": {
1601
- "optional": true
1602
- },
1603
- "@types/react-dom": {
1604
- "optional": true
1605
- }
1606
- }
1607
- },
1608
- "node_modules/@radix-ui/react-primitive": {
1609
- "version": "2.1.3",
1610
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
1611
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
1612
- "license": "MIT",
1613
- "dependencies": {
1614
- "@radix-ui/react-slot": "1.2.3"
1615
- },
1616
- "peerDependencies": {
1617
- "@types/react": "*",
1618
- "@types/react-dom": "*",
1619
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1620
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1621
- },
1622
- "peerDependenciesMeta": {
1623
- "@types/react": {
1624
- "optional": true
1625
- },
1626
- "@types/react-dom": {
1627
- "optional": true
1628
- }
1629
- }
1630
- },
1631
- "node_modules/@radix-ui/react-roving-focus": {
1632
- "version": "1.1.10",
1633
- "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz",
1634
- "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==",
1635
- "license": "MIT",
1636
- "dependencies": {
1637
- "@radix-ui/primitive": "1.1.2",
1638
- "@radix-ui/react-collection": "1.1.7",
1639
- "@radix-ui/react-compose-refs": "1.1.2",
1640
- "@radix-ui/react-context": "1.1.2",
1641
- "@radix-ui/react-direction": "1.1.1",
1642
- "@radix-ui/react-id": "1.1.1",
1643
- "@radix-ui/react-primitive": "2.1.3",
1644
- "@radix-ui/react-use-callback-ref": "1.1.1",
1645
- "@radix-ui/react-use-controllable-state": "1.2.2"
1646
- },
1647
- "peerDependencies": {
1648
- "@types/react": "*",
1649
- "@types/react-dom": "*",
1650
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1651
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1652
- },
1653
- "peerDependenciesMeta": {
1654
- "@types/react": {
1655
- "optional": true
1656
- },
1657
- "@types/react-dom": {
1658
- "optional": true
1659
- }
1660
- }
1661
- },
1662
- "node_modules/@radix-ui/react-select": {
1663
- "version": "2.2.5",
1664
- "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz",
1665
- "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==",
1666
- "license": "MIT",
1667
- "dependencies": {
1668
- "@radix-ui/number": "1.1.1",
1669
- "@radix-ui/primitive": "1.1.2",
1670
- "@radix-ui/react-collection": "1.1.7",
1671
- "@radix-ui/react-compose-refs": "1.1.2",
1672
- "@radix-ui/react-context": "1.1.2",
1673
- "@radix-ui/react-direction": "1.1.1",
1674
- "@radix-ui/react-dismissable-layer": "1.1.10",
1675
- "@radix-ui/react-focus-guards": "1.1.2",
1676
- "@radix-ui/react-focus-scope": "1.1.7",
1677
- "@radix-ui/react-id": "1.1.1",
1678
- "@radix-ui/react-popper": "1.2.7",
1679
- "@radix-ui/react-portal": "1.1.9",
1680
- "@radix-ui/react-primitive": "2.1.3",
1681
- "@radix-ui/react-slot": "1.2.3",
1682
- "@radix-ui/react-use-callback-ref": "1.1.1",
1683
- "@radix-ui/react-use-controllable-state": "1.2.2",
1684
- "@radix-ui/react-use-layout-effect": "1.1.1",
1685
- "@radix-ui/react-use-previous": "1.1.1",
1686
- "@radix-ui/react-visually-hidden": "1.2.3",
1687
- "aria-hidden": "^1.2.4",
1688
- "react-remove-scroll": "^2.6.3"
1689
- },
1690
- "peerDependencies": {
1691
- "@types/react": "*",
1692
- "@types/react-dom": "*",
1693
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1694
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1695
- },
1696
- "peerDependenciesMeta": {
1697
- "@types/react": {
1698
- "optional": true
1699
- },
1700
- "@types/react-dom": {
1701
- "optional": true
1702
- }
1703
- }
1704
- },
1705
- "node_modules/@radix-ui/react-slot": {
1706
- "version": "1.2.3",
1707
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
1708
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
1709
- "license": "MIT",
1710
- "dependencies": {
1711
- "@radix-ui/react-compose-refs": "1.1.2"
1712
- },
1713
- "peerDependencies": {
1714
- "@types/react": "*",
1715
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1716
- },
1717
- "peerDependenciesMeta": {
1718
- "@types/react": {
1719
- "optional": true
1720
- }
1721
- }
1722
- },
1723
- "node_modules/@radix-ui/react-switch": {
1724
- "version": "1.2.5",
1725
- "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz",
1726
- "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==",
1727
- "license": "MIT",
1728
- "dependencies": {
1729
- "@radix-ui/primitive": "1.1.2",
1730
- "@radix-ui/react-compose-refs": "1.1.2",
1731
- "@radix-ui/react-context": "1.1.2",
1732
- "@radix-ui/react-primitive": "2.1.3",
1733
- "@radix-ui/react-use-controllable-state": "1.2.2",
1734
- "@radix-ui/react-use-previous": "1.1.1",
1735
- "@radix-ui/react-use-size": "1.1.1"
1736
- },
1737
- "peerDependencies": {
1738
- "@types/react": "*",
1739
- "@types/react-dom": "*",
1740
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1741
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1742
- },
1743
- "peerDependenciesMeta": {
1744
- "@types/react": {
1745
- "optional": true
1746
- },
1747
- "@types/react-dom": {
1748
- "optional": true
1749
- }
1750
- }
1751
- },
1752
- "node_modules/@radix-ui/react-tabs": {
1753
- "version": "1.1.12",
1754
- "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz",
1755
- "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==",
1756
- "license": "MIT",
1757
- "dependencies": {
1758
- "@radix-ui/primitive": "1.1.2",
1759
- "@radix-ui/react-context": "1.1.2",
1760
- "@radix-ui/react-direction": "1.1.1",
1761
- "@radix-ui/react-id": "1.1.1",
1762
- "@radix-ui/react-presence": "1.1.4",
1763
- "@radix-ui/react-primitive": "2.1.3",
1764
- "@radix-ui/react-roving-focus": "1.1.10",
1765
- "@radix-ui/react-use-controllable-state": "1.2.2"
1766
- },
1767
- "peerDependencies": {
1768
- "@types/react": "*",
1769
- "@types/react-dom": "*",
1770
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1771
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1772
- },
1773
- "peerDependenciesMeta": {
1774
- "@types/react": {
1775
- "optional": true
1776
- },
1777
- "@types/react-dom": {
1778
- "optional": true
1779
- }
1780
- }
1781
- },
1782
- "node_modules/@radix-ui/react-toggle": {
1783
- "version": "1.1.9",
1784
- "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.9.tgz",
1785
- "integrity": "sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==",
1786
- "license": "MIT",
1787
- "dependencies": {
1788
- "@radix-ui/primitive": "1.1.2",
1789
- "@radix-ui/react-primitive": "2.1.3",
1790
- "@radix-ui/react-use-controllable-state": "1.2.2"
1791
- },
1792
- "peerDependencies": {
1793
- "@types/react": "*",
1794
- "@types/react-dom": "*",
1795
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1796
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1797
- },
1798
- "peerDependenciesMeta": {
1799
- "@types/react": {
1800
- "optional": true
1801
- },
1802
- "@types/react-dom": {
1803
- "optional": true
1804
- }
1805
- }
1806
- },
1807
- "node_modules/@radix-ui/react-toggle-group": {
1808
- "version": "1.1.10",
1809
- "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.10.tgz",
1810
- "integrity": "sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==",
1811
- "license": "MIT",
1812
- "dependencies": {
1813
- "@radix-ui/primitive": "1.1.2",
1814
- "@radix-ui/react-context": "1.1.2",
1815
- "@radix-ui/react-direction": "1.1.1",
1816
- "@radix-ui/react-primitive": "2.1.3",
1817
- "@radix-ui/react-roving-focus": "1.1.10",
1818
- "@radix-ui/react-toggle": "1.1.9",
1819
- "@radix-ui/react-use-controllable-state": "1.2.2"
1820
- },
1821
- "peerDependencies": {
1822
- "@types/react": "*",
1823
- "@types/react-dom": "*",
1824
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1825
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1826
- },
1827
- "peerDependenciesMeta": {
1828
- "@types/react": {
1829
- "optional": true
1830
- },
1831
- "@types/react-dom": {
1832
- "optional": true
1833
- }
1834
- }
1835
- },
1836
- "node_modules/@radix-ui/react-tooltip": {
1837
- "version": "1.2.7",
1838
- "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz",
1839
- "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==",
1840
- "license": "MIT",
1841
- "dependencies": {
1842
- "@radix-ui/primitive": "1.1.2",
1843
- "@radix-ui/react-compose-refs": "1.1.2",
1844
- "@radix-ui/react-context": "1.1.2",
1845
- "@radix-ui/react-dismissable-layer": "1.1.10",
1846
- "@radix-ui/react-id": "1.1.1",
1847
- "@radix-ui/react-popper": "1.2.7",
1848
- "@radix-ui/react-portal": "1.1.9",
1849
- "@radix-ui/react-presence": "1.1.4",
1850
- "@radix-ui/react-primitive": "2.1.3",
1851
- "@radix-ui/react-slot": "1.2.3",
1852
- "@radix-ui/react-use-controllable-state": "1.2.2",
1853
- "@radix-ui/react-visually-hidden": "1.2.3"
1854
- },
1855
- "peerDependencies": {
1856
- "@types/react": "*",
1857
- "@types/react-dom": "*",
1858
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1859
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1860
- },
1861
- "peerDependenciesMeta": {
1862
- "@types/react": {
1863
- "optional": true
1864
- },
1865
- "@types/react-dom": {
1866
- "optional": true
1867
- }
1868
- }
1869
- },
1870
- "node_modules/@radix-ui/react-use-callback-ref": {
1871
- "version": "1.1.1",
1872
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
1873
- "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
1874
- "license": "MIT",
1875
- "peerDependencies": {
1876
- "@types/react": "*",
1877
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1878
- },
1879
- "peerDependenciesMeta": {
1880
- "@types/react": {
1881
- "optional": true
1882
- }
1883
- }
1884
- },
1885
- "node_modules/@radix-ui/react-use-controllable-state": {
1886
- "version": "1.2.2",
1887
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
1888
- "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
1889
- "license": "MIT",
1890
- "dependencies": {
1891
- "@radix-ui/react-use-effect-event": "0.0.2",
1892
- "@radix-ui/react-use-layout-effect": "1.1.1"
1893
- },
1894
- "peerDependencies": {
1895
- "@types/react": "*",
1896
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1897
- },
1898
- "peerDependenciesMeta": {
1899
- "@types/react": {
1900
- "optional": true
1901
- }
1902
- }
1903
- },
1904
- "node_modules/@radix-ui/react-use-effect-event": {
1905
- "version": "0.0.2",
1906
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
1907
- "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
1908
- "license": "MIT",
1909
- "dependencies": {
1910
- "@radix-ui/react-use-layout-effect": "1.1.1"
1911
- },
1912
- "peerDependencies": {
1913
- "@types/react": "*",
1914
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1915
- },
1916
- "peerDependenciesMeta": {
1917
- "@types/react": {
1918
- "optional": true
1919
- }
1920
- }
1921
- },
1922
- "node_modules/@radix-ui/react-use-escape-keydown": {
1923
- "version": "1.1.1",
1924
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
1925
- "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
1926
- "license": "MIT",
1927
- "dependencies": {
1928
- "@radix-ui/react-use-callback-ref": "1.1.1"
1929
- },
1930
- "peerDependencies": {
1931
- "@types/react": "*",
1932
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1933
- },
1934
- "peerDependenciesMeta": {
1935
- "@types/react": {
1936
- "optional": true
1937
- }
1938
- }
1939
- },
1940
- "node_modules/@radix-ui/react-use-is-hydrated": {
1941
- "version": "0.1.0",
1942
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz",
1943
- "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==",
1944
- "license": "MIT",
1945
- "dependencies": {
1946
- "use-sync-external-store": "^1.5.0"
1947
- },
1948
- "peerDependencies": {
1949
- "@types/react": "*",
1950
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1951
- },
1952
- "peerDependenciesMeta": {
1953
- "@types/react": {
1954
- "optional": true
1955
- }
1956
- }
1957
- },
1958
- "node_modules/@radix-ui/react-use-layout-effect": {
1959
- "version": "1.1.1",
1960
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
1961
- "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
1962
- "license": "MIT",
1963
- "peerDependencies": {
1964
- "@types/react": "*",
1965
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1966
- },
1967
- "peerDependenciesMeta": {
1968
- "@types/react": {
1969
- "optional": true
1970
- }
1971
- }
1972
- },
1973
- "node_modules/@radix-ui/react-use-previous": {
1974
- "version": "1.1.1",
1975
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
1976
- "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
1977
- "license": "MIT",
1978
- "peerDependencies": {
1979
- "@types/react": "*",
1980
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1981
- },
1982
- "peerDependenciesMeta": {
1983
- "@types/react": {
1984
- "optional": true
1985
- }
1986
- }
1987
- },
1988
- "node_modules/@radix-ui/react-use-rect": {
1989
- "version": "1.1.1",
1990
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
1991
- "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
1992
- "license": "MIT",
1993
- "dependencies": {
1994
- "@radix-ui/rect": "1.1.1"
1995
- },
1996
- "peerDependencies": {
1997
- "@types/react": "*",
1998
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1999
- },
2000
- "peerDependenciesMeta": {
2001
- "@types/react": {
2002
- "optional": true
2003
- }
2004
- }
2005
- },
2006
- "node_modules/@radix-ui/react-use-size": {
2007
- "version": "1.1.1",
2008
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
2009
- "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
2010
- "license": "MIT",
2011
- "dependencies": {
2012
- "@radix-ui/react-use-layout-effect": "1.1.1"
2013
- },
2014
- "peerDependencies": {
2015
- "@types/react": "*",
2016
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2017
- },
2018
- "peerDependenciesMeta": {
2019
- "@types/react": {
2020
- "optional": true
2021
- }
2022
- }
2023
- },
2024
- "node_modules/@radix-ui/react-visually-hidden": {
2025
- "version": "1.2.3",
2026
- "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
2027
- "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
2028
- "license": "MIT",
2029
- "dependencies": {
2030
- "@radix-ui/react-primitive": "2.1.3"
2031
- },
2032
- "peerDependencies": {
2033
- "@types/react": "*",
2034
- "@types/react-dom": "*",
2035
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2036
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2037
- },
2038
- "peerDependenciesMeta": {
2039
- "@types/react": {
2040
- "optional": true
2041
- },
2042
- "@types/react-dom": {
2043
- "optional": true
2044
- }
2045
- }
2046
- },
2047
- "node_modules/@radix-ui/rect": {
2048
- "version": "1.1.1",
2049
- "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
2050
- "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
2051
- "license": "MIT"
2052
- },
2053
  "node_modules/@rollup/rollup-android-arm-eabi": {
2054
  "version": "4.36.0",
2055
  "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz",
@@ -2648,12 +1702,11 @@
2648
  "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
2649
  },
2650
  "node_modules/@types/node": {
2651
- "version": "22.15.21",
2652
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
2653
- "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
2654
- "license": "MIT",
2655
  "dependencies": {
2656
- "undici-types": "~6.21.0"
2657
  }
2658
  },
2659
  "node_modules/@types/qs": {
@@ -2680,7 +1733,7 @@
2680
  "version": "19.0.4",
2681
  "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
2682
  "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
2683
- "devOptional": true,
2684
  "peerDependencies": {
2685
  "@types/react": "^19.0.0"
2686
  }
@@ -3048,18 +2101,6 @@
3048
  "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
3049
  "dev": true
3050
  },
3051
- "node_modules/aria-hidden": {
3052
- "version": "1.2.6",
3053
- "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
3054
- "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
3055
- "license": "MIT",
3056
- "dependencies": {
3057
- "tslib": "^2.0.0"
3058
- },
3059
- "engines": {
3060
- "node": ">=10"
3061
- }
3062
- },
3063
  "node_modules/array-flatten": {
3064
  "version": "1.1.1",
3065
  "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -3424,18 +2465,6 @@
3424
  "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
3425
  "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
3426
  },
3427
- "node_modules/class-variance-authority": {
3428
- "version": "0.7.1",
3429
- "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
3430
- "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
3431
- "license": "Apache-2.0",
3432
- "dependencies": {
3433
- "clsx": "^2.1.1"
3434
- },
3435
- "funding": {
3436
- "url": "https://polar.sh/cva"
3437
- }
3438
- },
3439
  "node_modules/classnames": {
3440
  "version": "2.5.1",
3441
  "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
@@ -3445,7 +2474,6 @@
3445
  "version": "2.1.1",
3446
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
3447
  "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
3448
- "license": "MIT",
3449
  "engines": {
3450
  "node": ">=6"
3451
  }
@@ -3696,12 +2724,6 @@
3696
  "node": ">=8"
3697
  }
3698
  },
3699
- "node_modules/detect-node-es": {
3700
- "version": "1.1.0",
3701
- "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
3702
- "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
3703
- "license": "MIT"
3704
- },
3705
  "node_modules/devlop": {
3706
  "version": "1.1.0",
3707
  "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
@@ -4370,15 +3392,6 @@
4370
  "url": "https://github.com/sponsors/ljharb"
4371
  }
4372
  },
4373
- "node_modules/get-nonce": {
4374
- "version": "1.0.1",
4375
- "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
4376
- "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
4377
- "license": "MIT",
4378
- "engines": {
4379
- "node": ">=6"
4380
- }
4381
- },
4382
  "node_modules/get-proto": {
4383
  "version": "1.0.1",
4384
  "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
@@ -5088,15 +4101,6 @@
5088
  "yallist": "^3.0.2"
5089
  }
5090
  },
5091
- "node_modules/lucide-react": {
5092
- "version": "0.511.0",
5093
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz",
5094
- "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==",
5095
- "license": "ISC",
5096
- "peerDependencies": {
5097
- "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
5098
- }
5099
- },
5100
  "node_modules/math-intrinsics": {
5101
  "version": "1.1.0",
5102
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -5854,16 +4858,6 @@
5854
  "node": ">= 0.6"
5855
  }
5856
  },
5857
- "node_modules/next-themes": {
5858
- "version": "0.4.6",
5859
- "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
5860
- "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
5861
- "license": "MIT",
5862
- "peerDependencies": {
5863
- "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
5864
- "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
5865
- }
5866
- },
5867
  "node_modules/node-abi": {
5868
  "version": "3.74.0",
5869
  "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz",
@@ -6393,53 +5387,6 @@
6393
  "node": ">=0.10.0"
6394
  }
6395
  },
6396
- "node_modules/react-remove-scroll": {
6397
- "version": "2.7.0",
6398
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.0.tgz",
6399
- "integrity": "sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg==",
6400
- "license": "MIT",
6401
- "dependencies": {
6402
- "react-remove-scroll-bar": "^2.3.7",
6403
- "react-style-singleton": "^2.2.3",
6404
- "tslib": "^2.1.0",
6405
- "use-callback-ref": "^1.3.3",
6406
- "use-sidecar": "^1.1.3"
6407
- },
6408
- "engines": {
6409
- "node": ">=10"
6410
- },
6411
- "peerDependencies": {
6412
- "@types/react": "*",
6413
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
6414
- },
6415
- "peerDependenciesMeta": {
6416
- "@types/react": {
6417
- "optional": true
6418
- }
6419
- }
6420
- },
6421
- "node_modules/react-remove-scroll-bar": {
6422
- "version": "2.3.8",
6423
- "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
6424
- "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
6425
- "license": "MIT",
6426
- "dependencies": {
6427
- "react-style-singleton": "^2.2.2",
6428
- "tslib": "^2.0.0"
6429
- },
6430
- "engines": {
6431
- "node": ">=10"
6432
- },
6433
- "peerDependencies": {
6434
- "@types/react": "*",
6435
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
6436
- },
6437
- "peerDependenciesMeta": {
6438
- "@types/react": {
6439
- "optional": true
6440
- }
6441
- }
6442
- },
6443
  "node_modules/react-speech-recognition": {
6444
  "version": "4.0.0",
6445
  "resolved": "https://registry.npmjs.org/react-speech-recognition/-/react-speech-recognition-4.0.0.tgz",
@@ -6448,28 +5395,6 @@
6448
  "react": ">=16.8.0"
6449
  }
6450
  },
6451
- "node_modules/react-style-singleton": {
6452
- "version": "2.2.3",
6453
- "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
6454
- "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
6455
- "license": "MIT",
6456
- "dependencies": {
6457
- "get-nonce": "^1.0.0",
6458
- "tslib": "^2.0.0"
6459
- },
6460
- "engines": {
6461
- "node": ">=10"
6462
- },
6463
- "peerDependencies": {
6464
- "@types/react": "*",
6465
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
6466
- },
6467
- "peerDependenciesMeta": {
6468
- "@types/react": {
6469
- "optional": true
6470
- }
6471
- }
6472
- },
6473
  "node_modules/react-toastify": {
6474
  "version": "11.0.5",
6475
  "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
@@ -6950,16 +5875,6 @@
6950
  "is-arrayish": "^0.3.1"
6951
  }
6952
  },
6953
- "node_modules/sonner": {
6954
- "version": "2.0.3",
6955
- "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz",
6956
- "integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==",
6957
- "license": "MIT",
6958
- "peerDependencies": {
6959
- "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
6960
- "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
6961
- }
6962
- },
6963
  "node_modules/source-map": {
6964
  "version": "0.6.1",
6965
  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -7116,16 +6031,6 @@
7116
  "node": ">=8"
7117
  }
7118
  },
7119
- "node_modules/tailwind-merge": {
7120
- "version": "3.3.0",
7121
- "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.0.tgz",
7122
- "integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==",
7123
- "license": "MIT",
7124
- "funding": {
7125
- "type": "github",
7126
- "url": "https://github.com/sponsors/dcastil"
7127
- }
7128
- },
7129
  "node_modules/tailwindcss": {
7130
  "version": "4.0.15",
7131
  "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
@@ -7254,16 +6159,6 @@
7254
  "node": "*"
7255
  }
7256
  },
7257
- "node_modules/tw-animate-css": {
7258
- "version": "1.3.0",
7259
- "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.0.tgz",
7260
- "integrity": "sha512-jrJ0XenzS9KVuDThJDvnhalbl4IYiMQ/XvpA0a2FL8KmlK+6CSMviO7ROY/I7z1NnUs5NnDhlM6fXmF40xPxzw==",
7261
- "dev": true,
7262
- "license": "MIT",
7263
- "funding": {
7264
- "url": "https://github.com/sponsors/Wombosvideo"
7265
- }
7266
- },
7267
  "node_modules/type-check": {
7268
  "version": "0.4.0",
7269
  "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -7324,10 +6219,9 @@
7324
  }
7325
  },
7326
  "node_modules/undici-types": {
7327
- "version": "6.21.0",
7328
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
7329
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
7330
- "license": "MIT"
7331
  },
7332
  "node_modules/unified": {
7333
  "version": "11.0.5",
@@ -7457,58 +6351,6 @@
7457
  "punycode": "^2.1.0"
7458
  }
7459
  },
7460
- "node_modules/use-callback-ref": {
7461
- "version": "1.3.3",
7462
- "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
7463
- "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
7464
- "license": "MIT",
7465
- "dependencies": {
7466
- "tslib": "^2.0.0"
7467
- },
7468
- "engines": {
7469
- "node": ">=10"
7470
- },
7471
- "peerDependencies": {
7472
- "@types/react": "*",
7473
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
7474
- },
7475
- "peerDependenciesMeta": {
7476
- "@types/react": {
7477
- "optional": true
7478
- }
7479
- }
7480
- },
7481
- "node_modules/use-sidecar": {
7482
- "version": "1.1.3",
7483
- "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
7484
- "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
7485
- "license": "MIT",
7486
- "dependencies": {
7487
- "detect-node-es": "^1.1.0",
7488
- "tslib": "^2.0.0"
7489
- },
7490
- "engines": {
7491
- "node": ">=10"
7492
- },
7493
- "peerDependencies": {
7494
- "@types/react": "*",
7495
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
7496
- },
7497
- "peerDependenciesMeta": {
7498
- "@types/react": {
7499
- "optional": true
7500
- }
7501
- }
7502
- },
7503
- "node_modules/use-sync-external-store": {
7504
- "version": "1.5.0",
7505
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
7506
- "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
7507
- "license": "MIT",
7508
- "peerDependencies": {
7509
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
7510
- }
7511
- },
7512
  "node_modules/util-deprecate": {
7513
  "version": "1.0.2",
7514
  "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
 
9
  "version": "0.0.0",
10
  "dependencies": {
11
  "@huggingface/hub": "^1.1.1",
12
+ "@huggingface/inference": "^3.6.1",
13
  "@monaco-editor/react": "^4.7.0",
 
 
 
 
 
 
 
 
 
 
 
14
  "@tailwindcss/vite": "^4.0.15",
15
  "@xenova/transformers": "^2.17.2",
16
  "body-parser": "^1.20.3",
 
17
  "classnames": "^2.5.1",
 
18
  "cookie-parser": "^1.4.7",
19
  "dotenv": "^16.4.7",
20
  "express": "^4.21.2",
 
 
21
  "react": "^19.0.0",
22
  "react-dom": "^19.0.0",
23
  "react-icons": "^5.5.0",
 
25
  "react-speech-recognition": "^4.0.0",
26
  "react-toastify": "^11.0.5",
27
  "react-use": "^17.6.0",
 
 
28
  "tailwindcss": "^4.0.15"
29
  },
30
  "devDependencies": {
31
  "@eslint/js": "^9.21.0",
32
  "@types/express": "^5.0.1",
 
33
  "@types/react": "^19.0.10",
34
  "@types/react-dom": "^19.0.4",
35
  "@types/react-speech-recognition": "^3.9.6",
 
38
  "eslint-plugin-react-hooks": "^5.1.0",
39
  "eslint-plugin-react-refresh": "^0.4.19",
40
  "globals": "^15.15.0",
 
41
  "typescript": "~5.7.2",
42
  "typescript-eslint": "^8.24.1",
43
  "vite": "^6.2.0"
 
172
  }
173
  },
174
  "node_modules/@babel/helper-plugin-utils": {
175
+ "version": "7.26.5",
176
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
177
+ "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
178
  "dev": true,
 
179
  "engines": {
180
  "node": ">=6.9.0"
181
  }
 
845
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
846
  }
847
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
848
  "node_modules/@huggingface/hub": {
849
  "version": "1.1.1",
850
  "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-1.1.1.tgz",
 
862
  "integrity": "sha512-HK6JTVB/nrgjOnbe77HFSENftfAp67AI4mHMR2x64Os1hvchuTT88M8fKEiyESSvqKFKwW4lQKkHva07p05AXw=="
863
  },
864
  "node_modules/@huggingface/inference": {
865
+ "version": "3.6.1",
866
+ "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-3.6.1.tgz",
867
+ "integrity": "sha512-EtQlbBqcZycPe+qiTEFI+wNHOMpG0gwNTaZSvYu1juN1p/1dEgqAb2GO31dxLgNev2PzH9d+9nm8GngOsIepJg==",
 
868
  "dependencies": {
869
+ "@huggingface/jinja": "^0.3.3",
870
+ "@huggingface/tasks": "^0.17.8"
871
  },
872
  "engines": {
873
  "node": ">=18"
874
  }
875
  },
876
  "node_modules/@huggingface/jinja": {
877
+ "version": "0.3.3",
878
+ "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.3.3.tgz",
879
+ "integrity": "sha512-vQQr2JyWvVFba3Lj9es4q9vCl1sAc74fdgnEMoX8qHrXtswap9ge9uO3ONDzQB0cQ0PUyaKY2N6HaVbTBvSXvw==",
 
880
  "engines": {
881
  "node": ">=18"
882
  }
883
  },
884
  "node_modules/@huggingface/tasks": {
885
+ "version": "0.17.9",
886
+ "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.17.9.tgz",
887
+ "integrity": "sha512-lV6RgCJkqy3p93FFxP9H4SGJmFcHAwr1FO+Zk56q/JWsf7Tdsel1DEo1Xfd3An7ZPWpc2Y9ldRecGo9efDYghg=="
 
888
  },
889
  "node_modules/@humanfs/core": {
890
  "version": "0.19.1",
 
1104
  "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
1105
  "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
1106
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1107
  "node_modules/@rollup/rollup-android-arm-eabi": {
1108
  "version": "4.36.0",
1109
  "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz",
 
1702
  "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
1703
  },
1704
  "node_modules/@types/node": {
1705
+ "version": "22.13.13",
1706
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz",
1707
+ "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==",
 
1708
  "dependencies": {
1709
+ "undici-types": "~6.20.0"
1710
  }
1711
  },
1712
  "node_modules/@types/qs": {
 
1733
  "version": "19.0.4",
1734
  "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
1735
  "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
1736
+ "dev": true,
1737
  "peerDependencies": {
1738
  "@types/react": "^19.0.0"
1739
  }
 
2101
  "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
2102
  "dev": true
2103
  },
 
 
 
 
 
 
 
 
 
 
 
 
2104
  "node_modules/array-flatten": {
2105
  "version": "1.1.1",
2106
  "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
 
2465
  "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
2466
  "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
2467
  },
 
 
 
 
 
 
 
 
 
 
 
 
2468
  "node_modules/classnames": {
2469
  "version": "2.5.1",
2470
  "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
 
2474
  "version": "2.1.1",
2475
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
2476
  "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
 
2477
  "engines": {
2478
  "node": ">=6"
2479
  }
 
2724
  "node": ">=8"
2725
  }
2726
  },
 
 
 
 
 
 
2727
  "node_modules/devlop": {
2728
  "version": "1.1.0",
2729
  "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
 
3392
  "url": "https://github.com/sponsors/ljharb"
3393
  }
3394
  },
 
 
 
 
 
 
 
 
 
3395
  "node_modules/get-proto": {
3396
  "version": "1.0.1",
3397
  "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
 
4101
  "yallist": "^3.0.2"
4102
  }
4103
  },
 
 
 
 
 
 
 
 
 
4104
  "node_modules/math-intrinsics": {
4105
  "version": "1.1.0",
4106
  "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
 
4858
  "node": ">= 0.6"
4859
  }
4860
  },
 
 
 
 
 
 
 
 
 
 
4861
  "node_modules/node-abi": {
4862
  "version": "3.74.0",
4863
  "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz",
 
5387
  "node": ">=0.10.0"
5388
  }
5389
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5390
  "node_modules/react-speech-recognition": {
5391
  "version": "4.0.0",
5392
  "resolved": "https://registry.npmjs.org/react-speech-recognition/-/react-speech-recognition-4.0.0.tgz",
 
5395
  "react": ">=16.8.0"
5396
  }
5397
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5398
  "node_modules/react-toastify": {
5399
  "version": "11.0.5",
5400
  "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
 
5875
  "is-arrayish": "^0.3.1"
5876
  }
5877
  },
 
 
 
 
 
 
 
 
 
 
5878
  "node_modules/source-map": {
5879
  "version": "0.6.1",
5880
  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
 
6031
  "node": ">=8"
6032
  }
6033
  },
 
 
 
 
 
 
 
 
 
 
6034
  "node_modules/tailwindcss": {
6035
  "version": "4.0.15",
6036
  "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
 
6159
  "node": "*"
6160
  }
6161
  },
 
 
 
 
 
 
 
 
 
 
6162
  "node_modules/type-check": {
6163
  "version": "0.4.0",
6164
  "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
 
6219
  }
6220
  },
6221
  "node_modules/undici-types": {
6222
+ "version": "6.20.0",
6223
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
6224
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
 
6225
  },
6226
  "node_modules/unified": {
6227
  "version": "11.0.5",
 
6351
  "punycode": "^2.1.0"
6352
  }
6353
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6354
  "node_modules/util-deprecate": {
6355
  "version": "1.0.2",
6356
  "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
package.json CHANGED
@@ -12,30 +12,15 @@
12
  },
13
  "dependencies": {
14
  "@huggingface/hub": "^1.1.1",
15
- "@huggingface/inference": "^4.0.2",
16
  "@monaco-editor/react": "^4.7.0",
17
- "@radix-ui/react-avatar": "^1.1.10",
18
- "@radix-ui/react-dialog": "^1.1.14",
19
- "@radix-ui/react-dropdown-menu": "^2.1.15",
20
- "@radix-ui/react-popover": "^1.1.14",
21
- "@radix-ui/react-select": "^2.2.5",
22
- "@radix-ui/react-slot": "^1.2.3",
23
- "@radix-ui/react-switch": "^1.2.5",
24
- "@radix-ui/react-tabs": "^1.1.12",
25
- "@radix-ui/react-toggle": "^1.1.9",
26
- "@radix-ui/react-toggle-group": "^1.1.10",
27
- "@radix-ui/react-tooltip": "^1.2.7",
28
  "@tailwindcss/vite": "^4.0.15",
29
  "@xenova/transformers": "^2.17.2",
30
  "body-parser": "^1.20.3",
31
- "class-variance-authority": "^0.7.1",
32
  "classnames": "^2.5.1",
33
- "clsx": "^2.1.1",
34
  "cookie-parser": "^1.4.7",
35
  "dotenv": "^16.4.7",
36
  "express": "^4.21.2",
37
- "lucide-react": "^0.511.0",
38
- "next-themes": "^0.4.6",
39
  "react": "^19.0.0",
40
  "react-dom": "^19.0.0",
41
  "react-icons": "^5.5.0",
@@ -43,14 +28,11 @@
43
  "react-speech-recognition": "^4.0.0",
44
  "react-toastify": "^11.0.5",
45
  "react-use": "^17.6.0",
46
- "sonner": "^2.0.3",
47
- "tailwind-merge": "^3.3.0",
48
  "tailwindcss": "^4.0.15"
49
  },
50
  "devDependencies": {
51
  "@eslint/js": "^9.21.0",
52
  "@types/express": "^5.0.1",
53
- "@types/node": "^22.15.21",
54
  "@types/react": "^19.0.10",
55
  "@types/react-dom": "^19.0.4",
56
  "@types/react-speech-recognition": "^3.9.6",
@@ -59,7 +41,6 @@
59
  "eslint-plugin-react-hooks": "^5.1.0",
60
  "eslint-plugin-react-refresh": "^0.4.19",
61
  "globals": "^15.15.0",
62
- "tw-animate-css": "^1.3.0",
63
  "typescript": "~5.7.2",
64
  "typescript-eslint": "^8.24.1",
65
  "vite": "^6.2.0"
 
12
  },
13
  "dependencies": {
14
  "@huggingface/hub": "^1.1.1",
15
+ "@huggingface/inference": "^3.6.1",
16
  "@monaco-editor/react": "^4.7.0",
 
 
 
 
 
 
 
 
 
 
 
17
  "@tailwindcss/vite": "^4.0.15",
18
  "@xenova/transformers": "^2.17.2",
19
  "body-parser": "^1.20.3",
 
20
  "classnames": "^2.5.1",
 
21
  "cookie-parser": "^1.4.7",
22
  "dotenv": "^16.4.7",
23
  "express": "^4.21.2",
 
 
24
  "react": "^19.0.0",
25
  "react-dom": "^19.0.0",
26
  "react-icons": "^5.5.0",
 
28
  "react-speech-recognition": "^4.0.0",
29
  "react-toastify": "^11.0.5",
30
  "react-use": "^17.6.0",
 
 
31
  "tailwindcss": "^4.0.15"
32
  },
33
  "devDependencies": {
34
  "@eslint/js": "^9.21.0",
35
  "@types/express": "^5.0.1",
 
36
  "@types/react": "^19.0.10",
37
  "@types/react-dom": "^19.0.4",
38
  "@types/react-speech-recognition": "^3.9.6",
 
41
  "eslint-plugin-react-hooks": "^5.1.0",
42
  "eslint-plugin-react-refresh": "^0.4.19",
43
  "globals": "^15.15.0",
 
44
  "typescript": "~5.7.2",
45
  "typescript-eslint": "^8.24.1",
46
  "vite": "^6.2.0"
public/banner.png DELETED

Git LFS Details

  • SHA256: 88e110c7da99a0d6d17f1e301de3f6b77f1a2e3ea46fec3f4caed8334b0f0ba7
  • Pointer size: 130 Bytes
  • Size of remote file: 97.2 kB
public/providers/together.svg DELETED
server.js CHANGED
@@ -14,7 +14,7 @@ import { InferenceClient } from "@huggingface/inference";
14
  import bodyParser from "body-parser";
15
 
16
  import checkUser from "./middlewares/checkUser.js";
17
- import { MODELS, PROVIDERS } from "./utils/providers.js";
18
  import { COLORS } from "./utils/colors.js";
19
 
20
  // Load environment variables from .env file
@@ -30,12 +30,9 @@ const __dirname = path.dirname(__filename);
30
  const PORT = process.env.APP_PORT || 3000;
31
  const REDIRECT_URI =
32
  process.env.REDIRECT_URI || `http://localhost:${PORT}/auth/login`;
 
33
  const MAX_REQUESTS_PER_IP = 2;
34
 
35
- const SEARCH_START = "<<<<<<< SEARCH";
36
- const DIVIDER = "=======";
37
- const REPLACE_END = ">>>>>>> REPLACE";
38
-
39
  app.use(cookieParser());
40
  app.use(bodyParser.json());
41
  app.use(express.static(path.join(__dirname, "dist")));
@@ -204,9 +201,6 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
204
  await uploadFiles({
205
  repo,
206
  files,
207
- commitTitle: `${prompts[prompts.length - 1]} - ${
208
- prompts.length > 1 ? "Follow Up" : "Initial"
209
- } Deployment`,
210
  accessToken: hf_token,
211
  });
212
  return res.status(200).send({ ok: true, path: repo.name });
@@ -219,42 +213,16 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
219
  });
220
 
221
  app.post("/api/ask-ai", async (req, res) => {
222
- const { prompt, provider, model, redesignMarkdown } = req.body;
223
- if (!model) {
224
  return res.status(400).send({
225
  ok: false,
226
  message: "Missing required fields",
227
  });
228
  }
229
- if (!redesignMarkdown && !prompt) {
230
- return res.status(400).send({
231
- ok: false,
232
- message: "Missing required fields",
233
- });
234
- }
235
-
236
- const initialSystemPrompt = `ONLY USE HTML, CSS AND JAVASCRIPT. If you want to use ICON make sure to import the library first. Try to create the best UI possible by using only HTML, CSS and JAVASCRIPT. MAKE IT RESPONSIVE USING TAILWINDCSS. Use as much as you can TailwindCSS for the CSS, if you can't do something with TailwindCSS, then use custom CSS (make sure to import <script src="https://cdn.tailwindcss.com"></script> in the head). Also, try to ellaborate as much as you can, to create something unique. ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE`;
237
-
238
- const selectedModel = MODELS.find(
239
- (m) => m.value === model || m.label === model
240
- );
241
- if (!selectedModel) {
242
- return res.status(400).send({
243
- ok: false,
244
- message: "Invalid model selected",
245
- });
246
- }
247
- if (!selectedModel.providers.includes(provider) && provider !== "auto") {
248
- return res.status(400).send({
249
- ok: false,
250
- openSelectProvider: true,
251
- message: `The selected model does not support the ${provider} provider.`,
252
- });
253
- }
254
 
255
  let { hf_token } = req.cookies;
256
  let token = hf_token;
257
- let billTo = null;
258
 
259
  if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
260
  token = process.env.HF_TOKEN;
@@ -278,7 +246,6 @@ app.post("/api/ask-ai", async (req, res) => {
278
  }
279
 
280
  token = process.env.DEFAULT_HF_TOKEN;
281
- billTo = "huggingface";
282
  }
283
 
284
  // Set up response headers for streaming
@@ -290,11 +257,13 @@ app.post("/api/ask-ai", async (req, res) => {
290
  let completeResponse = "";
291
 
292
  let TOKENS_USED = prompt?.length;
 
 
293
 
294
  const DEFAULT_PROVIDER = PROVIDERS.novita;
295
  const selectedProvider =
296
  provider === "auto"
297
- ? PROVIDERS[selectedModel.autoProvider]
298
  : PROVIDERS[provider] ?? DEFAULT_PROVIDER;
299
 
300
  if (provider !== "auto" && TOKENS_USED >= selectedProvider.max_tokens) {
@@ -304,27 +273,43 @@ app.post("/api/ask-ai", async (req, res) => {
304
  message: `Context is too long. ${selectedProvider.name} allow ${selectedProvider.max_tokens} max tokens.`,
305
  });
306
  }
 
307
  try {
308
- const chatCompletion = client.chatCompletionStream(
309
- {
310
- model: selectedModel.value,
311
- provider: selectedProvider.id,
312
- messages: [
313
- {
314
- role: "system",
315
- content: initialSystemPrompt,
316
- },
317
- {
318
- role: "user",
319
- content: redesignMarkdown
320
- ? `Here is my current design as a markdown:\n\n${redesignMarkdown}\n\nNow, please create a new design based on this markdown.`
321
- : prompt,
322
- },
323
- ],
324
- max_tokens: selectedProvider.max_tokens,
325
- },
326
- billTo ? { billTo } : {}
327
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
 
329
  while (true) {
330
  const { done, value } = await chatCompletion.next();
@@ -333,37 +318,23 @@ app.post("/api/ask-ai", async (req, res) => {
333
  }
334
  const chunk = value.choices[0]?.delta?.content;
335
  if (chunk) {
336
- let newChunk = chunk;
337
- if (!selectedModel?.isThinker) {
338
- if (provider !== "sambanova") {
339
- res.write(chunk);
340
- completeResponse += chunk;
341
-
342
- if (completeResponse.includes("</html>")) {
343
- break;
344
- }
345
- } else {
346
- let newChunk = chunk;
347
- if (chunk.includes("</html>")) {
348
- newChunk = newChunk.replace(/<\/html>[\s\S]*/, "</html>");
349
- }
350
- completeResponse += newChunk;
351
- res.write(newChunk);
352
- if (newChunk.includes("</html>")) {
353
- break;
354
- }
355
  }
356
  } else {
357
- const lastThinkTagIndex = completeResponse.lastIndexOf("</think>");
 
 
 
 
358
  completeResponse += newChunk;
359
  res.write(newChunk);
360
- if (lastThinkTagIndex !== -1) {
361
- const afterLastThinkTag = completeResponse.slice(
362
- lastThinkTagIndex + "</think>".length
363
- );
364
- if (afterLastThinkTag.includes("</html>")) {
365
- break;
366
- }
367
  }
368
  }
369
  }
@@ -391,230 +362,6 @@ app.post("/api/ask-ai", async (req, res) => {
391
  }
392
  });
393
 
394
- app.put("/api/ask-ai", async (req, res) => {
395
- const { prompt, html, previousPrompt } = req.body;
396
- if (!prompt || !html) {
397
- return res.status(400).send({
398
- ok: false,
399
- message: "Missing required fields",
400
- });
401
- }
402
- const followUpSystemPrompt = `You are an expert web developer modifying an existing HTML file.
403
- The user wants to apply changes based on their request.
404
- You MUST output ONLY the changes required using the following SEARCH/REPLACE block format. Do NOT output the entire file.
405
- Explain the changes briefly *before* the blocks if necessary, but the code changes THEMSELVES MUST be within the blocks.
406
- Format Rules:
407
- 1. Start with ${SEARCH_START}
408
- 2. Provide the exact lines from the current code that need to be replaced.
409
- 3. Use ${DIVIDER} to separate the search block from the replacement.
410
- 4. Provide the new lines that should replace the original lines.
411
- 5. End with ${REPLACE_END}
412
- 6. You can use multiple SEARCH/REPLACE blocks if changes are needed in different parts of the file.
413
- 7. To insert code, use an empty SEARCH block (only ${SEARCH_START} and ${DIVIDER} on their lines) if inserting at the very beginning, otherwise provide the line *before* the insertion point in the SEARCH block and include that line plus the new lines in the REPLACE block.
414
- 8. To delete code, provide the lines to delete in the SEARCH block and leave the REPLACE block empty (only ${DIVIDER} and ${REPLACE_END} on their lines).
415
- 9. IMPORTANT: The SEARCH block must *exactly* match the current code, including indentation and whitespace.
416
- Example Modifying Code:
417
- \`\`\`
418
- Some explanation...
419
- ${SEARCH_START}
420
- <h1>Old Title</h1>
421
- ${DIVIDER}
422
- <h1>New Title</h1>
423
- ${REPLACE_END}
424
- ${SEARCH_START}
425
- </body>
426
- ${DIVIDER}
427
- <script>console.log("Added script");</script>
428
- </body>
429
- ${REPLACE_END}
430
- \`\`\`
431
- Example Deleting Code:
432
- \`\`\`
433
- Removing the paragraph...
434
- ${SEARCH_START}
435
- <p>This paragraph will be deleted.</p>
436
- ${DIVIDER}
437
- ${REPLACE_END}
438
- \`\`\``;
439
-
440
- // force to use deepseek-ai/DeepSeek-V3-0324 model, to avoid thinker models.
441
- const selectedModel = MODELS[0];
442
-
443
- let { hf_token } = req.cookies;
444
- let token = hf_token;
445
- let billTo = null;
446
-
447
- if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
448
- token = process.env.HF_TOKEN;
449
- }
450
-
451
- const ip =
452
- req.headers["x-forwarded-for"]?.split(",")[0].trim() ||
453
- req.headers["x-real-ip"] ||
454
- req.socket.remoteAddress ||
455
- req.ip ||
456
- "0.0.0.0";
457
-
458
- if (!token) {
459
- ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);
460
- if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {
461
- return res.status(429).send({
462
- ok: false,
463
- openLogin: true,
464
- message: "Log In to continue using the service",
465
- });
466
- }
467
-
468
- token = process.env.DEFAULT_HF_TOKEN;
469
- billTo = "huggingface";
470
- }
471
-
472
- const client = new InferenceClient(token);
473
-
474
- const selectedProvider = PROVIDERS[selectedModel.autoProvider];
475
- try {
476
- const response = await client.chatCompletion(
477
- {
478
- model: selectedModel.value,
479
- provider: selectedProvider.id,
480
- messages: [
481
- {
482
- role: "system",
483
- content: followUpSystemPrompt,
484
- },
485
- {
486
- role: "user",
487
- content: previousPrompt
488
- ? previousPrompt
489
- : "You are modifying the HTML file based on the user's request.",
490
- },
491
- {
492
- role: "assistant",
493
- content: `The current code is: \n\`\`\`html\n${html}\n\`\`\``,
494
- },
495
- {
496
- role: "user",
497
- content: prompt,
498
- },
499
- ],
500
- ...(selectedProvider.id !== "sambanova"
501
- ? {
502
- max_tokens: selectedProvider.max_tokens,
503
- }
504
- : {}),
505
- },
506
- billTo ? { billTo } : {}
507
- );
508
-
509
- const chunk = response.choices[0]?.message?.content;
510
- // TO DO: handle the case where there are multiple SEARCH/REPLACE blocks
511
- if (!chunk) {
512
- return res.status(400).send({
513
- ok: false,
514
- message: "No content returned from the model",
515
- });
516
- }
517
-
518
- if (chunk) {
519
- let newHtml = html;
520
- // array of arrays to hold updated lines (start and end line numbers)
521
- const updatedLines = [];
522
-
523
- // Find all search/replace blocks in the chunk
524
- let position = 0;
525
- let moreBlocks = true;
526
-
527
- while (moreBlocks) {
528
- const searchStartIndex = chunk.indexOf(SEARCH_START, position);
529
- if (searchStartIndex === -1) {
530
- moreBlocks = false;
531
- continue;
532
- }
533
-
534
- const dividerIndex = chunk.indexOf(DIVIDER, searchStartIndex);
535
- if (dividerIndex === -1) {
536
- moreBlocks = false;
537
- continue;
538
- }
539
-
540
- const replaceEndIndex = chunk.indexOf(REPLACE_END, dividerIndex);
541
- if (replaceEndIndex === -1) {
542
- moreBlocks = false;
543
- continue;
544
- }
545
-
546
- // Extract the search and replace blocks
547
- const searchBlock = chunk.substring(
548
- searchStartIndex + SEARCH_START.length,
549
- dividerIndex
550
- );
551
- const replaceBlock = chunk.substring(
552
- dividerIndex + DIVIDER.length,
553
- replaceEndIndex
554
- );
555
-
556
- // Apply the replacement
557
- if (searchBlock.trim() === "") {
558
- // Inserting at the beginning
559
- newHtml = `${replaceBlock}\n${newHtml}`;
560
-
561
- // Track first line as updated
562
- updatedLines.push([1, replaceBlock.split("\n").length]);
563
- } else {
564
- // Find the position of the search block in the HTML
565
- const blockPosition = newHtml.indexOf(searchBlock);
566
- if (blockPosition !== -1) {
567
- // Count lines before the search block
568
- const beforeText = newHtml.substring(0, blockPosition);
569
- const startLineNumber = beforeText.split("\n").length;
570
-
571
- // Count lines in search and replace blocks
572
- const replaceLines = replaceBlock.split("\n").length;
573
-
574
- // Calculate end line (start + length of replaced content)
575
- const endLineNumber = startLineNumber + replaceLines - 1;
576
-
577
- // Track the line numbers that were updated
578
- updatedLines.push([startLineNumber, endLineNumber]);
579
-
580
- // Perform the replacement
581
- newHtml = newHtml.replace(searchBlock, replaceBlock);
582
- }
583
- }
584
-
585
- // Move position to after this block to find the next one
586
- position = replaceEndIndex + REPLACE_END.length;
587
- }
588
-
589
- return res.status(200).send({
590
- ok: true,
591
- html: newHtml,
592
- updatedLines,
593
- });
594
- } else {
595
- return res.status(400).send({
596
- ok: false,
597
- message: "No content returned from the model",
598
- });
599
- }
600
- } catch (error) {
601
- if (error.message.includes("exceeded your monthly included credits")) {
602
- return res.status(402).send({
603
- ok: false,
604
- openProModal: true,
605
- message: error.message,
606
- });
607
- }
608
- if (!res.headersSent) {
609
- res.status(500).send({
610
- ok: false,
611
- message:
612
- error.message || "An error occurred while processing your request.",
613
- });
614
- }
615
- }
616
- });
617
-
618
  app.get("/api/remix/:username/:repo", async (req, res) => {
619
  const { username, repo } = req.params;
620
  const { hf_token } = req.cookies;
@@ -683,43 +430,6 @@ app.get("/api/remix/:username/:repo", async (req, res) => {
683
  });
684
  }
685
  });
686
-
687
- app.post("/api/re-design", async (req, res) => {
688
- const { url } = req.body;
689
- if (!url) {
690
- return res.status(400).send({
691
- ok: false,
692
- message: "Missing required fields",
693
- });
694
- }
695
-
696
- // call the api https://r.jina.ai/{url} and return the response
697
- try {
698
- const response = await fetch(
699
- `https://r.jina.ai/${encodeURIComponent(url)}`,
700
- {
701
- method: "POST",
702
- }
703
- );
704
- if (!response.ok) {
705
- return res.status(500).send({
706
- ok: false,
707
- message: "Failed to fetch redesign",
708
- });
709
- }
710
- // return the html response
711
- const markdown = await response.text();
712
- return res.status(200).send({
713
- ok: true,
714
- markdown,
715
- });
716
- } catch (error) {
717
- return res.status(500).send({
718
- ok: false,
719
- message: error.message,
720
- });
721
- }
722
- });
723
  app.get("*", (_req, res) => {
724
  res.sendFile(path.join(__dirname, "dist", "index.html"));
725
  });
 
14
  import bodyParser from "body-parser";
15
 
16
  import checkUser from "./middlewares/checkUser.js";
17
+ import { PROVIDERS } from "./utils/providers.js";
18
  import { COLORS } from "./utils/colors.js";
19
 
20
  // Load environment variables from .env file
 
30
  const PORT = process.env.APP_PORT || 3000;
31
  const REDIRECT_URI =
32
  process.env.REDIRECT_URI || `http://localhost:${PORT}/auth/login`;
33
+ const MODEL_ID = "deepseek-ai/DeepSeek-V3-0324";
34
  const MAX_REQUESTS_PER_IP = 2;
35
 
 
 
 
 
36
  app.use(cookieParser());
37
  app.use(bodyParser.json());
38
  app.use(express.static(path.join(__dirname, "dist")));
 
201
  await uploadFiles({
202
  repo,
203
  files,
 
 
 
204
  accessToken: hf_token,
205
  });
206
  return res.status(200).send({ ok: true, path: repo.name });
 
213
  });
214
 
215
  app.post("/api/ask-ai", async (req, res) => {
216
+ const { prompt, html, previousPrompt, provider } = req.body;
217
+ if (!prompt) {
218
  return res.status(400).send({
219
  ok: false,
220
  message: "Missing required fields",
221
  });
222
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  let { hf_token } = req.cookies;
225
  let token = hf_token;
 
226
 
227
  if (process.env.HF_TOKEN && process.env.HF_TOKEN !== "") {
228
  token = process.env.HF_TOKEN;
 
246
  }
247
 
248
  token = process.env.DEFAULT_HF_TOKEN;
 
249
  }
250
 
251
  // Set up response headers for streaming
 
257
  let completeResponse = "";
258
 
259
  let TOKENS_USED = prompt?.length;
260
+ if (previousPrompt) TOKENS_USED += previousPrompt.length;
261
+ if (html) TOKENS_USED += html.length;
262
 
263
  const DEFAULT_PROVIDER = PROVIDERS.novita;
264
  const selectedProvider =
265
  provider === "auto"
266
+ ? DEFAULT_PROVIDER
267
  : PROVIDERS[provider] ?? DEFAULT_PROVIDER;
268
 
269
  if (provider !== "auto" && TOKENS_USED >= selectedProvider.max_tokens) {
 
273
  message: `Context is too long. ${selectedProvider.name} allow ${selectedProvider.max_tokens} max tokens.`,
274
  });
275
  }
276
+
277
  try {
278
+ const chatCompletion = client.chatCompletionStream({
279
+ model: MODEL_ID,
280
+ provider: selectedProvider.id,
281
+ messages: [
282
+ {
283
+ role: "system",
284
+ content: `ONLY USE HTML, CSS AND JAVASCRIPT. If you want to use ICON make sure to import the library first. Try to create the best UI possible by using only HTML, CSS and JAVASCRIPT. Use as much as you can TailwindCSS for the CSS, if you can't do something with TailwindCSS, then use custom CSS (make sure to import <script src="https://cdn.tailwindcss.com"></script> in the head). Also, try to ellaborate as much as you can, to create something unique. ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE`,
285
+ },
286
+ ...(previousPrompt
287
+ ? [
288
+ {
289
+ role: "user",
290
+ content: previousPrompt,
291
+ },
292
+ ]
293
+ : []),
294
+ ...(html
295
+ ? [
296
+ {
297
+ role: "assistant",
298
+ content: `The current code is: ${html}.`,
299
+ },
300
+ ]
301
+ : []),
302
+ {
303
+ role: "user",
304
+ content: prompt,
305
+ },
306
+ ],
307
+ ...(selectedProvider.id !== "sambanova"
308
+ ? {
309
+ max_tokens: selectedProvider.max_tokens,
310
+ }
311
+ : {}),
312
+ });
313
 
314
  while (true) {
315
  const { done, value } = await chatCompletion.next();
 
318
  }
319
  const chunk = value.choices[0]?.delta?.content;
320
  if (chunk) {
321
+ if (provider !== "sambanova") {
322
+ res.write(chunk);
323
+ completeResponse += chunk;
324
+
325
+ if (completeResponse.includes("</html>")) {
326
+ break;
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  }
328
  } else {
329
+ let newChunk = chunk;
330
+ if (chunk.includes("</html>")) {
331
+ // Replace everything after the last </html> tag with an empty string
332
+ newChunk = newChunk.replace(/<\/html>[\s\S]*/, "</html>");
333
+ }
334
  completeResponse += newChunk;
335
  res.write(newChunk);
336
+ if (newChunk.includes("</html>")) {
337
+ break;
 
 
 
 
 
338
  }
339
  }
340
  }
 
362
  }
363
  });
364
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  app.get("/api/remix/:username/:repo", async (req, res) => {
366
  const { username, repo } = req.params;
367
  const { hf_token } = req.cookies;
 
430
  });
431
  }
432
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  app.get("*", (_req, res) => {
434
  res.sendFile(path.join(__dirname, "dist", "index.html"));
435
  });
src/assets/index.css CHANGED
@@ -1,7 +1,4 @@
1
  @import "tailwindcss";
2
- @import "tw-animate-css";
3
-
4
- @custom-variant dark (&:is(.dark *));
5
 
6
  * {
7
  font-family: "Noto Sans";
@@ -10,133 +7,3 @@
10
  .font-code {
11
  font-family: "Source Code Pro";
12
  }
13
-
14
- @theme inline {
15
- --radius-sm: calc(var(--radius) - 4px);
16
- --radius-md: calc(var(--radius) - 2px);
17
- --radius-lg: var(--radius);
18
- --radius-xl: calc(var(--radius) + 4px);
19
- --color-background: var(--background);
20
- --color-foreground: var(--foreground);
21
- --color-card: var(--card);
22
- --color-card-foreground: var(--card-foreground);
23
- --color-popover: var(--popover);
24
- --color-popover-foreground: var(--popover-foreground);
25
- --color-primary: var(--primary);
26
- --color-primary-foreground: var(--primary-foreground);
27
- --color-secondary: var(--secondary);
28
- --color-secondary-foreground: var(--secondary-foreground);
29
- --color-muted: var(--muted);
30
- --color-muted-foreground: var(--muted-foreground);
31
- --color-accent: var(--accent);
32
- --color-accent-foreground: var(--accent-foreground);
33
- --color-destructive: var(--destructive);
34
- --color-border: var(--border);
35
- --color-input: var(--input);
36
- --color-ring: var(--ring);
37
- --color-chart-1: var(--chart-1);
38
- --color-chart-2: var(--chart-2);
39
- --color-chart-3: var(--chart-3);
40
- --color-chart-4: var(--chart-4);
41
- --color-chart-5: var(--chart-5);
42
- --color-sidebar: var(--sidebar);
43
- --color-sidebar-foreground: var(--sidebar-foreground);
44
- --color-sidebar-primary: var(--sidebar-primary);
45
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
46
- --color-sidebar-accent: var(--sidebar-accent);
47
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
48
- --color-sidebar-border: var(--sidebar-border);
49
- --color-sidebar-ring: var(--sidebar-ring);
50
- }
51
-
52
- :root {
53
- --radius: 0.625rem;
54
- --background: oklch(1 0 0);
55
- --foreground: oklch(0.145 0 0);
56
- --card: oklch(1 0 0);
57
- --card-foreground: oklch(0.145 0 0);
58
- --popover: oklch(1 0 0);
59
- --popover-foreground: oklch(0.145 0 0);
60
- --primary: oklch(0.205 0 0);
61
- --primary-foreground: oklch(0.985 0 0);
62
- --secondary: oklch(0.97 0 0);
63
- --secondary-foreground: oklch(0.205 0 0);
64
- --muted: oklch(0.97 0 0);
65
- --muted-foreground: oklch(0.556 0 0);
66
- --accent: oklch(0.97 0 0);
67
- --accent-foreground: oklch(0.205 0 0);
68
- --destructive: oklch(0.577 0.245 27.325);
69
- --border: oklch(0.922 0 0);
70
- --input: oklch(0.922 0 0);
71
- --ring: oklch(0.708 0 0);
72
- --chart-1: oklch(0.646 0.222 41.116);
73
- --chart-2: oklch(0.6 0.118 184.704);
74
- --chart-3: oklch(0.398 0.07 227.392);
75
- --chart-4: oklch(0.828 0.189 84.429);
76
- --chart-5: oklch(0.769 0.188 70.08);
77
- --sidebar: oklch(0.985 0 0);
78
- --sidebar-foreground: oklch(0.145 0 0);
79
- --sidebar-primary: oklch(0.205 0 0);
80
- --sidebar-primary-foreground: oklch(0.985 0 0);
81
- --sidebar-accent: oklch(0.97 0 0);
82
- --sidebar-accent-foreground: oklch(0.205 0 0);
83
- --sidebar-border: oklch(0.922 0 0);
84
- --sidebar-ring: oklch(0.708 0 0);
85
- }
86
-
87
- .dark {
88
- --background: oklch(0.145 0 0);
89
- --foreground: oklch(0.985 0 0);
90
- --card: oklch(0.205 0 0);
91
- --card-foreground: oklch(0.985 0 0);
92
- --popover: oklch(0.205 0 0);
93
- --popover-foreground: oklch(0.985 0 0);
94
- --primary: oklch(0.922 0 0);
95
- --primary-foreground: oklch(0.205 0 0);
96
- --secondary: oklch(0.269 0 0);
97
- --secondary-foreground: oklch(0.985 0 0);
98
- --muted: oklch(0.269 0 0);
99
- --muted-foreground: oklch(0.708 0 0);
100
- --accent: oklch(0.269 0 0);
101
- --accent-foreground: oklch(0.985 0 0);
102
- --destructive: oklch(0.704 0.191 22.216);
103
- --border: oklch(1 0 0 / 10%);
104
- --input: oklch(1 0 0 / 15%);
105
- --ring: oklch(0.556 0 0);
106
- --chart-1: oklch(0.488 0.243 264.376);
107
- --chart-2: oklch(0.696 0.17 162.48);
108
- --chart-3: oklch(0.769 0.188 70.08);
109
- --chart-4: oklch(0.627 0.265 303.9);
110
- --chart-5: oklch(0.645 0.246 16.439);
111
- --sidebar: oklch(0.205 0 0);
112
- --sidebar-foreground: oklch(0.985 0 0);
113
- --sidebar-primary: oklch(0.488 0.243 264.376);
114
- --sidebar-primary-foreground: oklch(0.985 0 0);
115
- --sidebar-accent: oklch(0.269 0 0);
116
- --sidebar-accent-foreground: oklch(0.985 0 0);
117
- --sidebar-border: oklch(1 0 0 / 10%);
118
- --sidebar-ring: oklch(0.556 0 0);
119
- }
120
-
121
- @layer base {
122
- * {
123
- @apply border-border outline-ring/50;
124
- }
125
- body {
126
- @apply bg-background text-foreground;
127
- }
128
- }
129
-
130
- .monaco-editor .margin {
131
- @apply !bg-neutral-900;
132
- }
133
- .monaco-editor .monaco-editor-background {
134
- @apply !bg-neutral-900;
135
- }
136
- .monaco-editor .line-numbers {
137
- @apply !text-neutral-500;
138
- }
139
-
140
- .matched-line {
141
- @apply bg-sky-500/30;
142
- }
 
1
  @import "tailwindcss";
 
 
 
2
 
3
  * {
4
  font-family: "Noto Sans";
 
7
  .font-code {
8
  font-family: "Source Code Pro";
9
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/assets/linux.png DELETED
Binary file (8.63 kB)
 
src/{views → components}/App.tsx RENAMED
@@ -1,50 +1,45 @@
1
  import { useRef, useState } from "react";
 
 
 
2
  import {
3
- useCopyToClipboard,
 
4
  useEvent,
5
  useLocalStorage,
6
- useMount,
7
  useSearchParam,
8
- useUnmount,
9
- useUpdateEffect,
10
  } from "react-use";
11
- import Editor from "@monaco-editor/react";
12
- import { editor } from "monaco-editor";
13
- import { toast, Toaster } from "sonner";
14
- import classNames from "classnames";
15
- import { CopyIcon } from "lucide-react";
16
 
17
- import { ThemeProvider } from "../components/theme/theme-provider";
18
- import Header from "../components/header/header";
19
- import { Auth, HtmlHistory } from "../../utils/types";
20
- import { defaultHTML } from "../../utils/consts";
21
- import DeployButton from "../components/deploy-button/deploy-button";
22
- import Preview from "../components/preview/preview";
23
- import Footer from "../components/footer/footer";
24
- import AskAI from "../components/ask-ai/ask-ai";
25
 
26
- export default function App() {
27
  const [htmlStorage, , removeHtmlStorage] = useLocalStorage("html_content");
28
  const remix = useSearchParam("remix");
29
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
30
- const [_, copyToClipboard] = useCopyToClipboard();
31
 
32
  const preview = useRef<HTMLDivElement>(null);
33
  const editor = useRef<HTMLDivElement>(null);
34
  const resizer = useRef<HTMLDivElement>(null);
35
  const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
36
- const iframeRef = useRef<HTMLIFrameElement | null>(null);
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
- const monacoRef = useRef<any>(null);
39
 
 
 
40
  const [html, setHtml] = useState((htmlStorage as string) ?? defaultHTML);
41
  const [isAiWorking, setisAiWorking] = useState(false);
42
  const [auth, setAuth] = useState<Auth | undefined>(undefined);
 
 
 
43
  const [prompts, setPrompts] = useState<string[]>([]);
44
- const [device, setDevice] = useState<"desktop" | "mobile">("desktop");
45
- const [htmlHistory, setHtmlHistory] = useState<HtmlHistory[]>([]);
46
- const [currentTab, setCurrentTab] = useState("chat");
47
- const [isResizing, setIsResizing] = useState(false);
48
 
49
  const fetchMe = async () => {
50
  const res = await fetch("/api/@me");
@@ -132,20 +127,36 @@ export default function App() {
132
  document.removeEventListener("mouseup", handleMouseUp);
133
  };
134
 
 
 
 
 
 
 
 
 
 
135
  useMount(() => {
 
136
  fetchMe();
137
  fetchRemix();
138
 
 
139
  if (htmlStorage) {
140
  removeHtmlStorage();
141
- toast.warning("Previous HTML content restored from local storage.");
142
  }
143
 
 
144
  resetLayout();
 
 
145
  if (!resizer.current) return;
146
  resizer.current.addEventListener("mousedown", handleMouseDown);
147
  window.addEventListener("resize", resetLayout);
148
  });
 
 
149
  useUnmount(() => {
150
  document.removeEventListener("mousemove", handleResize);
151
  document.removeEventListener("mouseup", handleMouseUp);
@@ -155,178 +166,118 @@ export default function App() {
155
  window.removeEventListener("resize", resetLayout);
156
  });
157
 
158
- // Prevent accidental navigation away when AI is working or content has changed
159
- useEvent("beforeunload", (e) => {
160
- if (isAiWorking || html !== defaultHTML) {
161
- e.preventDefault();
162
- return "";
163
- }
164
- });
165
-
166
- useUpdateEffect(() => {
167
- if (currentTab === "chat") {
168
- // Reset editor width when switching to reasoning tab
169
- resetLayout();
170
- // re-add the event listener for resizing
171
- if (resizer.current) {
172
- resizer.current.addEventListener("mousedown", handleMouseDown);
173
- }
174
- } else {
175
- if (preview.current) {
176
- // Reset preview width when switching to preview tab
177
- preview.current.style.width = "100%";
178
- }
179
- }
180
- }, [currentTab]);
181
-
182
  return (
183
- <ThemeProvider
184
- defaultTheme="dark"
185
- storageKey="deepsite-ui-theme"
186
- className="h-screen bg-slate-100 dark:bg-neutral-950 flex flex-col"
187
- >
188
- <Header tab={currentTab} onNewTab={setCurrentTab}>
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  <DeployButton
190
  html={html}
 
191
  auth={auth}
192
  setHtml={setHtml}
193
  prompts={prompts}
194
  />
195
  </Header>
196
- <main className="bg-neutral-950 flex-1 max-lg:flex-col flex w-full">
197
- {currentTab === "chat" && (
198
- <>
199
- <div ref={editor} className="relative">
200
- <CopyIcon
201
- className="size-4 absolute top-2 right-5 text-neutral-500 hover:text-neutral-300 z-2 cursor-pointer"
202
- onClick={() => {
203
- copyToClipboard(html);
204
- toast.success("HTML copied to clipboard!");
205
- }}
206
- />
207
- <Editor
208
- language="html"
209
- theme="vs-dark"
210
- className={classNames(
211
- "h-[calc(100dvh-98px)] lg:h-full bg-neutral-900 transition-all duration-200 ",
212
- {
213
- "pointer-events-none": isAiWorking,
214
- }
215
- )}
216
- options={{
217
- colorDecorators: true,
218
- fontLigatures: true,
219
- theme: "vs-dark",
220
- minimap: { enabled: false },
221
- scrollbar: {
222
- horizontal: "hidden",
223
- },
224
- }}
225
- value={html}
226
- onChange={(value) => {
227
- const newValue = value ?? "";
228
- setHtml(newValue);
229
- }}
230
- onMount={(editor, monaco) => {
231
- editorRef.current = editor;
232
- monacoRef.current = monaco;
233
- }}
234
- />
235
- <AskAI
236
- html={html}
237
- setHtml={(newHtml: string) => {
238
- setHtml(newHtml);
239
- }}
240
- htmlHistory={htmlHistory}
241
- onSuccess={(
242
- finalHtml: string,
243
- p: string,
244
- updatedLines?: number[][]
245
- ) => {
246
- const currentHistory = [...htmlHistory];
247
- currentHistory.unshift({
248
- html: finalHtml,
249
- createdAt: new Date(),
250
- prompt: p,
251
- });
252
- setHtmlHistory(currentHistory);
253
- // if xs or sm
254
- if (window.innerWidth <= 1024) {
255
- setCurrentTab("preview");
256
- }
257
- if (updatedLines && updatedLines?.length > 0) {
258
- const decorations = updatedLines.map((line) => ({
259
- range: new monacoRef.current.Range(
260
- line[0],
261
- 1,
262
- line[1],
263
- 1
264
- ),
265
- options: {
266
- inlineClassName: "matched-line",
267
- },
268
- }));
269
- setTimeout(() => {
270
- editorRef?.current
271
- ?.getModel()
272
- ?.deltaDecorations([], decorations);
273
-
274
- editorRef.current?.revealLine(updatedLines[0][0]);
275
- }, 100);
276
- }
277
- }}
278
- isAiWorking={isAiWorking}
279
- setisAiWorking={setisAiWorking}
280
- onNewPrompt={(prompt: string) => {
281
- setPrompts((prev) => [...prev, prompt]);
282
- }}
283
- onScrollToBottom={() => {
284
- editorRef.current?.revealLine(
285
- editorRef.current?.getModel()?.getLineCount() ?? 0
286
- );
287
- }}
288
- />
289
- </div>
290
- <div
291
- ref={resizer}
292
- className="bg-neutral-800 hover:bg-sky-500 active:bg-sky-500 w-1.5 cursor-col-resize h-full max-lg:hidden"
293
  />
294
- </>
295
- )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  <Preview
297
  html={html}
298
  isResizing={isResizing}
299
  isAiWorking={isAiWorking}
300
  ref={preview}
301
- device={device}
302
- currentTab={currentTab}
303
- iframeRef={iframeRef}
304
  />
305
  </main>
306
- <Footer
307
- onReset={() => {
308
- if (isAiWorking) {
309
- toast.warning("Please wait for the AI to finish working.");
310
- return;
311
- }
312
- if (
313
- window.confirm("You're about to reset the editor. Are you sure?")
314
- ) {
315
- setHtml(defaultHTML);
316
- removeHtmlStorage();
317
- editorRef.current?.revealLine(
318
- editorRef.current?.getModel()?.getLineCount() ?? 0
319
- );
320
- }
321
- }}
322
- htmlHistory={htmlHistory}
323
- setHtml={setHtml}
324
- iframeRef={iframeRef}
325
- auth={auth}
326
- device={device}
327
- setDevice={setDevice}
328
- />
329
- <Toaster richColors position="bottom-center" />
330
- </ThemeProvider>
331
  );
332
  }
 
 
 
1
  import { useRef, useState } from "react";
2
+ import Editor from "@monaco-editor/react";
3
+ import classNames from "classnames";
4
+ import { editor } from "monaco-editor";
5
  import {
6
+ useMount,
7
+ useUnmount,
8
  useEvent,
9
  useLocalStorage,
 
10
  useSearchParam,
 
 
11
  } from "react-use";
12
+ import { toast } from "react-toastify";
 
 
 
 
13
 
14
+ import Header from "./header/header";
15
+ import DeployButton from "./deploy-button/deploy-button";
16
+ import { defaultHTML } from "./../../utils/consts";
17
+ import Tabs from "./tabs/tabs";
18
+ import AskAI from "./ask-ai/ask-ai";
19
+ import { Auth } from "./../../utils/types";
20
+ import Preview from "./preview/preview";
 
21
 
22
+ function App() {
23
  const [htmlStorage, , removeHtmlStorage] = useLocalStorage("html_content");
24
  const remix = useSearchParam("remix");
 
 
25
 
26
  const preview = useRef<HTMLDivElement>(null);
27
  const editor = useRef<HTMLDivElement>(null);
28
  const resizer = useRef<HTMLDivElement>(null);
29
  const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
 
 
 
30
 
31
+ const [isResizing, setIsResizing] = useState(false);
32
+ const [error, setError] = useState(false);
33
  const [html, setHtml] = useState((htmlStorage as string) ?? defaultHTML);
34
  const [isAiWorking, setisAiWorking] = useState(false);
35
  const [auth, setAuth] = useState<Auth | undefined>(undefined);
36
+ const [currentView, setCurrentView] = useState<"editor" | "preview">(
37
+ "editor"
38
+ );
39
  const [prompts, setPrompts] = useState<string[]>([]);
40
+ const [htmlHistory, setHtmlHistory] = useState<
41
+ { html: string; createdAt: Date; prompt: string }[]
42
+ >([]);
 
43
 
44
  const fetchMe = async () => {
45
  const res = await fetch("/api/@me");
 
127
  document.removeEventListener("mouseup", handleMouseUp);
128
  };
129
 
130
+ // Prevent accidental navigation away when AI is working or content has changed
131
+ useEvent("beforeunload", (e) => {
132
+ if (isAiWorking || html !== defaultHTML) {
133
+ e.preventDefault();
134
+ return "";
135
+ }
136
+ });
137
+
138
+ // Initialize component on mount
139
  useMount(() => {
140
+ // Fetch user data
141
  fetchMe();
142
  fetchRemix();
143
 
144
+ // Restore content from storage if available
145
  if (htmlStorage) {
146
  removeHtmlStorage();
147
+ toast.warn("Previous HTML content restored from local storage.");
148
  }
149
 
150
+ // Set initial layout based on window size
151
  resetLayout();
152
+
153
+ // Attach event listeners
154
  if (!resizer.current) return;
155
  resizer.current.addEventListener("mousedown", handleMouseDown);
156
  window.addEventListener("resize", resetLayout);
157
  });
158
+
159
+ // Clean up event listeners on unmount
160
  useUnmount(() => {
161
  document.removeEventListener("mousemove", handleResize);
162
  document.removeEventListener("mouseup", handleMouseUp);
 
166
  window.removeEventListener("resize", resetLayout);
167
  });
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  return (
170
+ <div className="h-screen bg-gray-950 font-sans overflow-hidden">
171
+ <Header
172
+ onReset={() => {
173
+ if (isAiWorking) {
174
+ toast.warn("Please wait for the AI to finish working.");
175
+ return;
176
+ }
177
+ if (
178
+ window.confirm("You're about to reset the editor. Are you sure?")
179
+ ) {
180
+ setHtml(defaultHTML);
181
+ setError(false);
182
+ removeHtmlStorage();
183
+ editorRef.current?.revealLine(
184
+ editorRef.current?.getModel()?.getLineCount() ?? 0
185
+ );
186
+ }
187
+ }}
188
+ >
189
  <DeployButton
190
  html={html}
191
+ error={error}
192
  auth={auth}
193
  setHtml={setHtml}
194
  prompts={prompts}
195
  />
196
  </Header>
197
+ <main className="max-lg:flex-col flex w-full">
198
+ <div
199
+ ref={editor}
200
+ className={classNames(
201
+ "w-full h-[calc(100dvh-49px)] lg:h-[calc(100dvh-54px)] relative overflow-hidden max-lg:transition-all max-lg:duration-200 select-none",
202
+ {
203
+ "max-lg:h-0": currentView === "preview",
204
+ }
205
+ )}
206
+ >
207
+ <Tabs htmlHistory={htmlHistory} setHtml={setHtml} />
208
+ <div
209
+ onClick={(e) => {
210
+ if (isAiWorking) {
211
+ e.preventDefault();
212
+ e.stopPropagation();
213
+ toast.warn("Please wait for the AI to finish working.");
214
+ }
215
+ }}
216
+ >
217
+ <Editor
218
+ language="html"
219
+ theme="vs-dark"
220
+ className={classNames(
221
+ "h-[calc(100dvh-90px)] lg:h-[calc(100dvh-96px)]",
222
+ {
223
+ "pointer-events-none": isAiWorking,
224
+ }
225
+ )}
226
+ value={html}
227
+ onValidate={(markers) => {
228
+ if (markers?.length > 0) {
229
+ setError(true);
230
+ }
231
+ }}
232
+ onChange={(value) => {
233
+ const newValue = value ?? "";
234
+ setHtml(newValue);
235
+ setError(false);
236
+ }}
237
+ onMount={(editor) => (editorRef.current = editor)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  />
239
+ </div>
240
+ <AskAI
241
+ html={html}
242
+ setHtml={(newHtml: string) => {
243
+ setHtml(newHtml);
244
+ }}
245
+ onSuccess={(finalHtml: string, p: string) => {
246
+ const currentHistory = [...htmlHistory];
247
+ currentHistory.unshift({
248
+ html: finalHtml,
249
+ createdAt: new Date(),
250
+ prompt: p,
251
+ });
252
+ setHtmlHistory(currentHistory);
253
+ }}
254
+ isAiWorking={isAiWorking}
255
+ setisAiWorking={setisAiWorking}
256
+ setView={setCurrentView}
257
+ onNewPrompt={(prompt) => {
258
+ setPrompts((prev) => [...prev, prompt]);
259
+ }}
260
+ onScrollToBottom={() => {
261
+ editorRef.current?.revealLine(
262
+ editorRef.current?.getModel()?.getLineCount() ?? 0
263
+ );
264
+ }}
265
+ />
266
+ </div>
267
+ <div
268
+ ref={resizer}
269
+ className="bg-gray-700 hover:bg-blue-500 w-2 cursor-col-resize h-[calc(100dvh-53px)] max-lg:hidden"
270
+ />
271
  <Preview
272
  html={html}
273
  isResizing={isResizing}
274
  isAiWorking={isAiWorking}
275
  ref={preview}
276
+ setView={setCurrentView}
 
 
277
  />
278
  </main>
279
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  );
281
  }
282
+
283
+ export default App;
src/components/ask-ai/ask-ai.tsx CHANGED
@@ -1,23 +1,19 @@
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { useState, useRef } from "react";
 
 
3
  import classNames from "classnames";
4
- import { toast } from "sonner";
5
- import { useLocalStorage, useUpdateEffect } from "react-use";
6
- import { ArrowUp, ChevronDown } from "lucide-react";
7
- import { FaStopCircle } from "react-icons/fa";
8
 
9
  import Login from "../login/login";
10
- import { defaultHTML } from "../../../utils/consts";
11
  import SuccessSound from "./../../assets/success.mp3";
12
  import Settings from "../settings/settings";
13
  import ProModal from "../pro-modal/pro-modal";
14
- import { Button } from "../ui/button";
15
- // @ts-expect-error not needed
16
- import { MODELS } from "../../../utils/providers";
17
- import Loading from "../loading/loading";
18
- import { HtmlHistory } from "../../../utils/types";
19
- import InviteFriends from "../invite-friends/invite-friends";
20
- import ReImagine from "../re-imagine/re-imagine";
21
 
22
  function AskAI({
23
  html,
@@ -25,6 +21,7 @@ function AskAI({
25
  onScrollToBottom,
26
  isAiWorking,
27
  setisAiWorking,
 
28
  onNewPrompt,
29
  onSuccess,
30
  }: {
@@ -33,198 +30,115 @@ function AskAI({
33
  onScrollToBottom: () => void;
34
  isAiWorking: boolean;
35
  onNewPrompt: (prompt: string) => void;
36
- htmlHistory?: HtmlHistory[];
37
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
38
- onSuccess: (h: string, p: string, n?: number[][]) => void;
39
  }) {
40
- const refThink = useRef<HTMLDivElement | null>(null);
41
-
42
  const [open, setOpen] = useState(false);
43
  const [prompt, setPrompt] = useState("");
44
  const [hasAsked, setHasAsked] = useState(false);
45
  const [previousPrompt, setPreviousPrompt] = useState("");
46
  const [provider, setProvider] = useLocalStorage("provider", "auto");
47
- const [model, setModel] = useLocalStorage("model", MODELS[0].value);
48
  const [openProvider, setOpenProvider] = useState(false);
49
  const [providerError, setProviderError] = useState("");
50
  const [openProModal, setOpenProModal] = useState(false);
51
- const [think, setThink] = useState<string | undefined>(undefined);
52
- const [openThink, setOpenThink] = useState(false);
53
- const [isThinking, setIsThinking] = useState(true);
54
- const [controller, setController] = useState<AbortController | null>(null);
55
 
56
  const audio = new Audio(SuccessSound);
57
  audio.volume = 0.5;
58
 
59
- const callAi = async (redesignMarkdown?: string) => {
60
- if (isAiWorking) return;
61
- if (!redesignMarkdown && !prompt.trim()) return;
62
  setisAiWorking(true);
63
  setProviderError("");
64
- setThink("");
65
- setOpenThink(false);
66
- setIsThinking(true);
67
 
68
  let contentResponse = "";
69
- let thinkResponse = "";
70
  let lastRenderTime = 0;
71
-
72
- const isFollowUp = html !== defaultHTML;
73
- const abortController = new AbortController();
74
- setController(abortController);
75
  try {
76
  onNewPrompt(prompt);
77
- if (isFollowUp && !redesignMarkdown) {
78
- const request = await fetch("/api/ask-ai", {
79
- method: "PUT",
80
- body: JSON.stringify({
81
- prompt,
82
- provider,
83
- previousPrompt,
84
- html,
85
- }),
86
- headers: {
87
- "Content-Type": "application/json",
88
- },
89
- signal: abortController.signal,
90
- });
91
- if (request && request.body) {
92
  const res = await request.json();
93
- if (!request.ok) {
94
- if (res.openLogin) {
95
- setOpen(true);
96
- } else if (res.openSelectProvider) {
97
- setOpenProvider(true);
98
- setProviderError(res.message);
99
- } else if (res.openProModal) {
100
- setOpenProModal(true);
101
- } else {
102
- toast.error(res.message);
103
- }
104
- setisAiWorking(false);
105
- return;
106
  }
107
- setHtml(res.html);
108
- toast.success("AI responded successfully");
109
- setPreviousPrompt(prompt);
110
- setPrompt("");
111
  setisAiWorking(false);
112
- onSuccess(res.html, prompt, res.updatedLines);
113
- audio.play();
114
  }
115
- } else {
116
- const request = await fetch("/api/ask-ai", {
117
- method: "POST",
118
- body: JSON.stringify({
119
- prompt,
120
- provider,
121
- model,
122
- redesignMarkdown,
123
- }),
124
- headers: {
125
- "Content-Type": "application/json",
126
- },
127
- signal: abortController.signal,
128
- });
129
- if (request && request.body) {
130
- if (!request.ok) {
131
- const res = await request.json();
132
- if (res.openLogin) {
133
- setOpen(true);
134
- } else if (res.openSelectProvider) {
135
- setOpenProvider(true);
136
- setProviderError(res.message);
137
- } else if (res.openProModal) {
138
- setOpenProModal(true);
139
- } else {
140
- toast.error(res.message);
141
- }
142
  setisAiWorking(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  return;
144
  }
145
- const reader = request.body.getReader();
146
- const decoder = new TextDecoder("utf-8");
147
- const selectedModel = MODELS.find(
148
- (m: { value: string }) => m.value === model
149
- );
150
- let contentThink: string | undefined = undefined;
151
- const read = async () => {
152
- const { done, value } = await reader.read();
153
- if (done) {
154
- toast.success("AI responded successfully");
155
- setPreviousPrompt(prompt);
156
- setPrompt("");
157
- setisAiWorking(false);
158
- setHasAsked(true);
159
- audio.play();
160
-
161
- // Now we have the complete HTML including </html>, so set it to be sure
162
- const finalDoc = contentResponse.match(
163
- /<!DOCTYPE html>[\s\S]*<\/html>/
164
- )?.[0];
165
- if (finalDoc) {
166
- setHtml(finalDoc);
167
- }
168
- onSuccess(finalDoc ?? contentResponse, prompt);
169
 
170
- return;
 
 
 
 
 
 
 
171
  }
172
 
173
- const chunk = decoder.decode(value, { stream: true });
174
- thinkResponse += chunk;
175
- if (selectedModel?.isThinker) {
176
- const thinkMatch = thinkResponse.match(/<think>[\s\S]*/)?.[0];
177
- if (thinkMatch && !thinkResponse?.includes("</think>")) {
178
- if ((contentThink?.length ?? 0) < 3) {
179
- setOpenThink(true);
180
- }
181
- setThink(thinkMatch.replace("<think>", "").trim());
182
- contentThink += chunk;
183
- return read();
184
- }
185
  }
186
 
187
- contentResponse += chunk;
188
-
189
- const newHtml = contentResponse.match(
190
- /<!DOCTYPE html>[\s\S]*/
191
- )?.[0];
192
- if (newHtml) {
193
- setIsThinking(false);
194
- let partialDoc = newHtml;
195
- if (
196
- partialDoc.includes("<head>") &&
197
- !partialDoc.includes("</head>")
198
- ) {
199
- partialDoc += "\n</head>";
200
- }
201
- if (
202
- partialDoc.includes("<body") &&
203
- !partialDoc.includes("</body>")
204
- ) {
205
- partialDoc += "\n</body>";
206
- }
207
- if (!partialDoc.includes("</html>")) {
208
- partialDoc += "\n</html>";
209
- }
210
-
211
- // Throttle the re-renders to avoid flashing/flicker
212
- const now = Date.now();
213
- if (now - lastRenderTime > 300) {
214
- setHtml(partialDoc);
215
- lastRenderTime = now;
216
- }
217
-
218
- if (partialDoc.length > 200) {
219
- onScrollToBottom();
220
- }
221
  }
222
- read();
223
- };
224
-
225
  read();
226
- }
 
 
227
  }
 
 
228
  } catch (error: any) {
229
  setisAiWorking(false);
230
  toast.error(error.message);
@@ -234,124 +148,65 @@ function AskAI({
234
  }
235
  };
236
 
237
- const stopController = () => {
238
- if (controller) {
239
- controller.abort();
240
- setController(null);
241
- setisAiWorking(false);
242
- setThink("");
243
- setOpenThink(false);
244
- setIsThinking(false);
245
- }
246
- };
247
-
248
- useUpdateEffect(() => {
249
- if (refThink.current) {
250
- refThink.current.scrollTop = refThink.current.scrollHeight;
251
- }
252
- }, [think]);
253
-
254
- useUpdateEffect(() => {
255
- if (!isThinking) {
256
- setOpenThink(false);
257
- }
258
- }, [isThinking]);
259
-
260
  return (
261
- <div className="bg-neutral-800 border border-neutral-700 rounded-2xl ring-[4px] focus-within:ring-neutral-500/30 focus-within:border-neutral-600 ring-transparent z-10 absolute bottom-3 left-3 w-[calc(100%-20px)] group">
262
- {think && (
263
- <div className="w-full border-b border-neutral-700 relative overflow-hidden">
264
- <header
265
- className="flex items-center justify-between px-5 py-2.5 group hover:bg-neutral-600/20 transition-colors duration-200 cursor-pointer"
266
- onClick={() => {
267
- setOpenThink(!openThink);
268
- }}
269
- >
270
- <p className="text-sm font-medium text-neutral-300 group-hover:text-neutral-200 transition-colors duration-200">
271
- {isThinking ? "DeepSite is thinking..." : "DeepSite's plan"}
272
- </p>
273
- <ChevronDown
274
- className={classNames(
275
- "size-4 text-neutral-400 group-hover:text-neutral-300 transition-all duration-200",
276
- {
277
- "rotate-180": openThink,
278
- }
279
- )}
280
- />
281
- </header>
282
- <main
283
- ref={refThink}
284
- className={classNames(
285
- "overflow-y-auto transition-all duration-200 ease-in-out",
286
- {
287
- "max-h-[0px]": !openThink,
288
- "min-h-[250px] max-h-[250px] border-t border-neutral-700":
289
- openThink,
290
- }
291
- )}
292
- >
293
- <p className="text-[13px] text-neutral-400 whitespace-pre-line px-5 pb-4 pt-3">
294
- {think}
295
- </p>
296
- </main>
297
- </div>
298
  )}
299
  <div className="w-full relative flex items-center justify-between">
300
- {isAiWorking && (
301
- <div className="absolute bg-neutral-800 rounded-lg bottom-0 left-4 w-[calc(100%-30px)] h-full z-1 flex items-center justify-between max-lg:text-sm">
302
- <div className="flex items-center justify-start gap-2">
303
- <Loading overlay={false} className="!size-4" />
304
- <p className="text-neutral-400 text-sm">
305
- AI is {isThinking ? "thinking" : "coding"}...{" "}
306
- </p>
307
- </div>
308
- <div
309
- className="text-xs text-neutral-400 px-1 py-0.5 rounded-md border border-neutral-600 flex items-center justify-center gap-1.5 bg-neutral-800 hover:brightness-110 transition-all duration-200 cursor-pointer"
310
- onClick={stopController}
311
- >
312
- <FaStopCircle />
313
- Stop generation
314
- </div>
315
- </div>
316
- )}
317
  <input
318
  type="text"
319
  disabled={isAiWorking}
320
- className="w-full bg-transparent text-sm outline-none text-white placeholder:text-neutral-400 p-4"
321
  placeholder={
322
- hasAsked ? "Ask DeepSite for edits" : "Ask DeepSite anything..."
323
  }
324
  value={prompt}
325
  onChange={(e) => setPrompt(e.target.value)}
326
  onKeyDown={(e) => {
327
- if (e.key === "Enter" && !e.shiftKey) {
328
  callAi();
329
  }
330
  }}
331
  />
332
- </div>
333
- <div className="flex items-center justify-between gap-2 px-4 pb-3">
334
- <div className="flex-1 flex items-center justify-start gap-1.5">
335
- <ReImagine onRedesign={(md) => callAi(md)} />
336
- <InviteFriends />
337
- </div>
338
  <div className="flex items-center justify-end gap-2">
 
339
  <Settings
340
  provider={provider as string}
341
- model={model as string}
342
  onChange={setProvider}
343
- onModelChange={setModel}
344
  open={openProvider}
345
  error={providerError}
346
  onClose={setOpenProvider}
347
  />
348
- <Button
349
- size="iconXs"
350
- disabled={isAiWorking || !prompt.trim()}
351
- onClick={() => callAi()}
352
  >
353
- <ArrowUp className="size-4" />
354
- </Button>
355
  </div>
356
  </div>
357
  <div
@@ -365,7 +220,7 @@ function AskAI({
365
  ></div>
366
  <div
367
  className={classNames(
368
- "absolute top-0 -translate-y-[calc(100%+8px)] right-0 z-10 w-80 border border-neutral-800 !bg-neutral-900 rounded-lg shadow-lg transition-all duration-75 overflow-hidden",
369
  {
370
  "opacity-0 pointer-events-none": !open,
371
  }
 
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { useState } from "react";
3
+ import { RiSparkling2Fill } from "react-icons/ri";
4
+ import { GrSend } from "react-icons/gr";
5
  import classNames from "classnames";
6
+ import { toast } from "react-toastify";
7
+ import { useCopyToClipboard, useLocalStorage } from "react-use";
8
+ import { MdPreview } from "react-icons/md";
9
+ import { IoCopy } from "react-icons/io5";
10
 
11
  import Login from "../login/login";
12
+ import { defaultHTML } from "./../../../utils/consts";
13
  import SuccessSound from "./../../assets/success.mp3";
14
  import Settings from "../settings/settings";
15
  import ProModal from "../pro-modal/pro-modal";
16
+ // import SpeechPrompt from "../speech-prompt/speech-prompt";
 
 
 
 
 
 
17
 
18
  function AskAI({
19
  html,
 
21
  onScrollToBottom,
22
  isAiWorking,
23
  setisAiWorking,
24
+ setView,
25
  onNewPrompt,
26
  onSuccess,
27
  }: {
 
30
  onScrollToBottom: () => void;
31
  isAiWorking: boolean;
32
  onNewPrompt: (prompt: string) => void;
33
+ setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
34
  setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
35
+ onSuccess: (h: string, p: string) => void;
36
  }) {
 
 
37
  const [open, setOpen] = useState(false);
38
  const [prompt, setPrompt] = useState("");
39
  const [hasAsked, setHasAsked] = useState(false);
40
  const [previousPrompt, setPreviousPrompt] = useState("");
41
  const [provider, setProvider] = useLocalStorage("provider", "auto");
 
42
  const [openProvider, setOpenProvider] = useState(false);
43
  const [providerError, setProviderError] = useState("");
44
  const [openProModal, setOpenProModal] = useState(false);
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ const [_, copyToClipboard] = useCopyToClipboard();
 
 
47
 
48
  const audio = new Audio(SuccessSound);
49
  audio.volume = 0.5;
50
 
51
+ const callAi = async () => {
52
+ if (isAiWorking || !prompt.trim()) return;
 
53
  setisAiWorking(true);
54
  setProviderError("");
 
 
 
55
 
56
  let contentResponse = "";
 
57
  let lastRenderTime = 0;
 
 
 
 
58
  try {
59
  onNewPrompt(prompt);
60
+ const request = await fetch("/api/ask-ai", {
61
+ method: "POST",
62
+ body: JSON.stringify({
63
+ prompt,
64
+ provider,
65
+ ...(html === defaultHTML ? {} : { html }),
66
+ ...(previousPrompt ? { previousPrompt } : {}),
67
+ }),
68
+ headers: {
69
+ "Content-Type": "application/json",
70
+ },
71
+ });
72
+ if (request && request.body) {
73
+ if (!request.ok) {
 
74
  const res = await request.json();
75
+ if (res.openLogin) {
76
+ setOpen(true);
77
+ } else if (res.openSelectProvider) {
78
+ setOpenProvider(true);
79
+ setProviderError(res.message);
80
+ } else if (res.openProModal) {
81
+ setOpenProModal(true);
82
+ } else {
83
+ toast.error(res.message);
 
 
 
 
84
  }
 
 
 
 
85
  setisAiWorking(false);
86
+ return;
 
87
  }
88
+ const reader = request.body.getReader();
89
+ const decoder = new TextDecoder("utf-8");
90
+
91
+ const read = async () => {
92
+ const { done, value } = await reader.read();
93
+ if (done) {
94
+ toast.success("AI responded successfully");
95
+ setPreviousPrompt(prompt);
96
+ setPrompt("");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  setisAiWorking(false);
98
+ setHasAsked(true);
99
+ audio.play();
100
+ setView("preview");
101
+
102
+ // Now we have the complete HTML including </html>, so set it to be sure
103
+ const finalDoc = contentResponse.match(
104
+ /<!DOCTYPE html>[\s\S]*<\/html>/
105
+ )?.[0];
106
+ if (finalDoc) {
107
+ setHtml(finalDoc);
108
+ }
109
+ onSuccess(finalDoc ?? contentResponse, prompt);
110
+
111
  return;
112
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ const chunk = decoder.decode(value, { stream: true });
115
+ contentResponse += chunk;
116
+ const newHtml = contentResponse.match(/<!DOCTYPE html>[\s\S]*/)?.[0];
117
+ if (newHtml) {
118
+ // Force-close the HTML tag so the iframe doesn't render half-finished markup
119
+ let partialDoc = newHtml;
120
+ if (!partialDoc.includes("</html>")) {
121
+ partialDoc += "\n</html>";
122
  }
123
 
124
+ // Throttle the re-renders to avoid flashing/flicker
125
+ const now = Date.now();
126
+ if (now - lastRenderTime > 300) {
127
+ setHtml(partialDoc);
128
+ lastRenderTime = now;
 
 
 
 
 
 
 
129
  }
130
 
131
+ if (partialDoc.length > 200) {
132
+ onScrollToBottom();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  }
134
+ }
 
 
135
  read();
136
+ };
137
+
138
+ read();
139
  }
140
+
141
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
  } catch (error: any) {
143
  setisAiWorking(false);
144
  toast.error(error.message);
 
148
  }
149
  };
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  return (
152
+ <div
153
+ className={`bg-gray-950 rounded-xl py-2 lg:py-2.5 pl-3.5 lg:pl-4 pr-2 lg:pr-2.5 absolute lg:sticky bottom-3 left-3 lg:bottom-4 lg:left-4 w-[calc(100%-1.5rem)] lg:w-[calc(100%-2rem)] z-10 group ${
154
+ isAiWorking ? "animate-pulse" : ""
155
+ }`}
156
+ >
157
+ {defaultHTML !== html && (
158
+ <p
159
+ className="text-xl text-white/50 hover:text-white/80 -translate-y-[calc(100%+8px)] absolute top-0 right-0 cursor-pointer"
160
+ onClick={() => {
161
+ copyToClipboard(html);
162
+ toast.success("HTML copied to clipboard");
163
+ }}
164
+ >
165
+ <IoCopy />
166
+ </p>
167
+ )}
168
+ {defaultHTML !== html && (
169
+ <button
170
+ className="bg-white lg:hidden -translate-y-[calc(100%+8px)] absolute left-0 top-0 shadow-md text-gray-950 text-xs font-medium py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-100 hover:brightness-150 transition-all duration-100 cursor-pointer"
171
+ onClick={() => setView("preview")}
172
+ >
173
+ <MdPreview className="text-sm" />
174
+ View Preview
175
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  )}
177
  <div className="w-full relative flex items-center justify-between">
178
+ <RiSparkling2Fill className="text-lg lg:text-xl text-gray-500 group-focus-within:text-pink-500" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  <input
180
  type="text"
181
  disabled={isAiWorking}
182
+ className="w-full bg-transparent max-lg:text-sm outline-none px-3 text-white placeholder:text-gray-500 font-code"
183
  placeholder={
184
+ hasAsked ? "What do you want to ask AI next?" : "Ask AI anything..."
185
  }
186
  value={prompt}
187
  onChange={(e) => setPrompt(e.target.value)}
188
  onKeyDown={(e) => {
189
+ if (e.key === "Enter") {
190
  callAi();
191
  }
192
  }}
193
  />
 
 
 
 
 
 
194
  <div className="flex items-center justify-end gap-2">
195
+ {/* <SpeechPrompt setPrompt={setPrompt} /> */}
196
  <Settings
197
  provider={provider as string}
 
198
  onChange={setProvider}
 
199
  open={openProvider}
200
  error={providerError}
201
  onClose={setOpenProvider}
202
  />
203
+ <button
204
+ disabled={isAiWorking}
205
+ className="relative overflow-hidden cursor-pointer flex-none flex items-center justify-center rounded-full text-sm font-semibold size-8 text-center bg-pink-500 hover:bg-pink-400 text-white shadow-sm dark:shadow-highlight/20 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed disabled:hover:bg-gray-300"
206
+ onClick={callAi}
207
  >
208
+ <GrSend className="-translate-x-[1px]" />
209
+ </button>
210
  </div>
211
  </div>
212
  <div
 
220
  ></div>
221
  <div
222
  className={classNames(
223
+ "absolute top-0 -translate-y-[calc(100%+8px)] right-0 z-10 w-80 bg-white border border-gray-200 rounded-lg shadow-lg transition-all duration-75 overflow-hidden",
224
  {
225
  "opacity-0 pointer-events-none": !open,
226
  }
src/components/deploy-button/deploy-button.tsx CHANGED
@@ -1,26 +1,43 @@
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
  import { useState } from "react";
3
- import { toast } from "sonner";
 
 
4
 
5
  import SpaceIcon from "@/assets/space.svg";
6
  import Loading from "../loading/loading";
7
  import Login from "../login/login";
8
  import { Auth } from "./../../../utils/types";
9
  import LoadButton from "../load-button/load-button";
10
- import { Button } from "../ui/button";
11
- import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  function DeployButton({
14
  html,
 
15
  auth,
16
  setHtml,
17
  prompts,
18
  }: {
19
  html: string;
 
20
  auth?: Auth;
21
  setHtml: (html: string) => void;
22
  prompts: string[];
23
  }) {
 
24
  const [loading, setLoading] = useState(false);
25
  const [path, setPath] = useState<string | undefined>(undefined);
26
 
@@ -46,17 +63,14 @@ function DeployButton({
46
  });
47
  const response = await request.json();
48
  if (response.ok) {
49
- toast.success("Your space is live! 🎉", {
50
- action: {
51
- label: "See Space",
52
- onClick: () => {
53
- window.open(
54
- `https://huggingface.co/spaces/${response.path ?? path}`,
55
- "_blank"
56
- );
57
- },
58
- },
59
- });
60
  setPath(response.path);
61
  } else {
62
  toast.error(response.message);
@@ -65,6 +79,7 @@ function DeployButton({
65
  toast.error(err.message);
66
  } finally {
67
  setLoading(false);
 
68
  }
69
  };
70
 
@@ -72,85 +87,135 @@ function DeployButton({
72
  <div className="flex items-center justify-end gap-5">
73
  <LoadButton auth={auth} setHtml={setHtml} setPath={setPath} />
74
  <div className="relative flex items-center justify-end">
75
- <Popover>
76
- <PopoverTrigger asChild>
77
- <div>
78
- <Button variant="pink" className="max-lg:hidden">
79
- {path ? "Update Space" : "Deploy to Space"}
80
- </Button>
81
- <Button variant="pink" size="sm" className="lg:hidden">
82
- {path ? "Update" : "Deploy"}
83
- </Button>
84
- </div>
85
- </PopoverTrigger>
86
- <PopoverContent
87
- className="!rounded-2xl p-0 overflow-hidden !bg-neutral-900"
88
- align="end"
89
- >
90
- {!auth ? (
91
- <Login html={html}>
92
- <p className="text-muted-foreground text-sm mb-3">
93
- Host this project for free and share it with your friends.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  </p>
95
- </Login>
96
- ) : (
97
- <>
98
- <header className="flex items-center text-sm px-4 py-3 border-b gap-2 bg-neutral-950 border-neutral-800 font-semibold text-neutral-200">
99
- <span className="text-xs bg-pink-500/10 text-pink-500 rounded-full pl-1.5 pr-2.5 py-0.5 flex items-center justify-start gap-1.5">
100
- <img src={SpaceIcon} alt="Space Icon" className="size-4" />
101
- Space
102
- </span>
103
- Configure Deployment
104
- </header>
105
- <main className="px-4 pt-3 pb-4 space-y-3">
106
- <p className="text-xs text-amber-600 bg-amber-500/10 rounded-md p-2">
107
- {path ? (
108
- <span>
109
- Your space is live at{" "}
110
- <a
111
- href={`https://huggingface.co/spaces/${path}`}
112
- target="_blank"
113
- className="underline hover:text-amber-700"
114
- >
115
- huggingface.co/{path}
116
- </a>
117
- . You can update it by deploying again.
118
- </span>
119
- ) : (
120
- "Deploy your project to a space on the Hub. Spaces are a way to share your project with the world."
121
- )}
122
  </p>
123
- {!path && (
124
- <label className="block">
125
- <p className="text-muted-foreground text-sm mb-1.5">
126
- Space Title
127
- </p>
128
- <input
129
- type="text"
130
- value={config.title}
131
- className="mr-2 border rounded-md px-3 py-1.5 border-gray-300 w-full text-sm"
132
- placeholder="My Awesome Space"
133
- onChange={(e) =>
134
- setConfig({ ...config, title: e.target.value })
135
- }
136
- />
137
- </label>
138
- )}
139
- <div className="pt-2 text-right">
140
- <button
141
- disabled={loading || (!path && !config.title)}
142
- className="relative rounded-full bg-black px-5 py-2 text-white font-semibold text-xs hover:bg-black/90 transition-all duration-100 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed disabled:hover:bg-gray-300"
143
- onClick={createSpace}
144
- >
145
- {path ? "Update Space" : "Create Space"}
146
- {loading && <Loading />}
147
- </button>
148
- </div>
149
- </main>
150
- </>
151
- )}
152
- </PopoverContent>
153
- </Popover>
154
  </div>
155
  </div>
156
  );
 
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
  import { useState } from "react";
3
+ import classNames from "classnames";
4
+ import { toast } from "react-toastify";
5
+ import { FaPowerOff } from "react-icons/fa6";
6
 
7
  import SpaceIcon from "@/assets/space.svg";
8
  import Loading from "../loading/loading";
9
  import Login from "../login/login";
10
  import { Auth } from "./../../../utils/types";
11
  import LoadButton from "../load-button/load-button";
12
+
13
+ const MsgToast = ({ url }: { url: string }) => (
14
+ <div className="w-full flex items-center justify-center gap-3">
15
+ Your space is live!
16
+ <button
17
+ className="bg-black text-sm block text-white rounded-md px-3 py-1.5 hover:bg-gray-900 cursor-pointer"
18
+ onClick={() => {
19
+ window.open(url, "_blank");
20
+ }}
21
+ >
22
+ See Space
23
+ </button>
24
+ </div>
25
+ );
26
 
27
  function DeployButton({
28
  html,
29
+ error = false,
30
  auth,
31
  setHtml,
32
  prompts,
33
  }: {
34
  html: string;
35
+ error: boolean;
36
  auth?: Auth;
37
  setHtml: (html: string) => void;
38
  prompts: string[];
39
  }) {
40
+ const [open, setOpen] = useState(false);
41
  const [loading, setLoading] = useState(false);
42
  const [path, setPath] = useState<string | undefined>(undefined);
43
 
 
63
  });
64
  const response = await request.json();
65
  if (response.ok) {
66
+ toast.success(
67
+ <MsgToast
68
+ url={`https://huggingface.co/spaces/${response.path ?? path}`}
69
+ />,
70
+ {
71
+ autoClose: 10000,
72
+ }
73
+ );
 
 
 
74
  setPath(response.path);
75
  } else {
76
  toast.error(response.message);
 
79
  toast.error(err.message);
80
  } finally {
81
  setLoading(false);
82
+ setOpen(false);
83
  }
84
  };
85
 
 
87
  <div className="flex items-center justify-end gap-5">
88
  <LoadButton auth={auth} setHtml={setHtml} setPath={setPath} />
89
  <div className="relative flex items-center justify-end">
90
+ {auth &&
91
+ (auth.isLocalUse ? (
92
+ <>
93
+ <div className="bg-amber-500/10 border border-amber-10 text-amber-500 font-semibold leading-5 lg:leading-6 py-1 px-5 text-xs lg:text-sm rounded-md mr-4 select-none">
94
+ Local Usage
95
+ </div>
96
+ </>
97
+ ) : (
98
+ <>
99
+ <button
100
+ className="mr-2 cursor-pointer"
101
+ onClick={() => {
102
+ if (confirm("Are you sure you want to log out?")) {
103
+ // go to /auth/logout page
104
+ window.location.href = "/auth/logout";
105
+ }
106
+ }}
107
+ >
108
+ <FaPowerOff className="text-lg text-red-500" />
109
+ </button>
110
+ <p className="mr-3 text-xs lg:text-sm text-gray-300">
111
+ <span className="max-lg:hidden">Connected as </span>
112
+ <a
113
+ href={`https://huggingface.co/${auth.preferred_username}`}
114
+ target="_blank"
115
+ className="underline hover:text-white"
116
+ >
117
+ {auth.preferred_username}
118
+ </a>
119
+ </p>
120
+ </>
121
+ ))}
122
+ <button
123
+ className={classNames(
124
+ "relative cursor-pointer flex-none flex items-center justify-center rounded-md text-xs lg:text-sm font-semibold leading-5 lg:leading-6 py-1.5 px-5 hover:bg-pink-400 text-white shadow-sm dark:shadow-highlight/20",
125
+ {
126
+ "bg-pink-400": open,
127
+ "bg-pink-500": !open,
128
+ }
129
+ )}
130
+ onClick={() => setOpen(!open)}
131
+ >
132
+ {path ? "Update Space" : "Deploy to Space"}
133
+ </button>
134
+ <div
135
+ className={classNames(
136
+ "h-screen w-screen bg-black/20 fixed left-0 top-0 z-10",
137
+ {
138
+ "opacity-0 pointer-events-none": !open,
139
+ }
140
+ )}
141
+ onClick={() => setOpen(false)}
142
+ ></div>
143
+ <div
144
+ className={classNames(
145
+ "absolute top-[calc(100%+8px)] right-0 z-10 w-80 bg-white border border-gray-200 rounded-lg shadow-lg transition-all duration-75 overflow-hidden",
146
+ {
147
+ "opacity-0 pointer-events-none": !open,
148
+ }
149
+ )}
150
+ >
151
+ {!auth ? (
152
+ <Login html={html}>
153
+ <p className="text-gray-500 text-sm mb-3">
154
+ Host this project for free and share it with your friends.
155
+ </p>
156
+ </Login>
157
+ ) : (
158
+ <>
159
+ <header className="flex items-center text-sm px-4 py-2 border-b border-gray-200 gap-2 bg-gray-100 font-semibold text-gray-700">
160
+ <span className="text-xs bg-pink-500/10 text-pink-500 rounded-full pl-1.5 pr-2.5 py-0.5 flex items-center justify-start gap-1.5">
161
+ <img src={SpaceIcon} alt="Space Icon" className="size-4" />
162
+ Space
163
+ </span>
164
+ Configure Deployment
165
+ </header>
166
+ <main className="px-4 pt-3 pb-4 space-y-3">
167
+ <p className="text-xs text-amber-600 bg-amber-500/10 rounded-md p-2">
168
+ {path ? (
169
+ <span>
170
+ Your space is live at{" "}
171
+ <a
172
+ href={`https://huggingface.co/spaces/${path}`}
173
+ target="_blank"
174
+ className="underline hover:text-amber-700"
175
+ >
176
+ huggingface.co/{path}
177
+ </a>
178
+ . You can update it by deploying again.
179
+ </span>
180
+ ) : (
181
+ "Deploy your project to a space on the Hub. Spaces are a way to share your project with the world."
182
+ )}
183
  </p>
184
+ {!path && (
185
+ <label className="block">
186
+ <p className="text-gray-600 text-sm font-medium mb-1.5">
187
+ Space Title
188
+ </p>
189
+ <input
190
+ type="text"
191
+ value={config.title}
192
+ className="mr-2 border rounded-md px-3 py-1.5 border-gray-300 w-full text-sm"
193
+ placeholder="My Awesome Space"
194
+ onChange={(e) =>
195
+ setConfig({ ...config, title: e.target.value })
196
+ }
197
+ />
198
+ </label>
199
+ )}
200
+ {error && (
201
+ <p className="text-red-500 text-xs bg-red-500/10 rounded-md p-2">
202
+ Your code has errors. Fix them before deploying.
 
 
 
 
 
 
 
 
203
  </p>
204
+ )}
205
+ <div className="pt-2 text-right">
206
+ <button
207
+ disabled={error || loading || (!path && !config.title)}
208
+ className="relative rounded-full bg-black px-5 py-2 text-white font-semibold text-xs hover:bg-black/90 transition-all duration-100 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed disabled:hover:bg-gray-300"
209
+ onClick={createSpace}
210
+ >
211
+ {path ? "Update Space" : "Create Space"}
212
+ {loading && <Loading />}
213
+ </button>
214
+ </div>
215
+ </main>
216
+ </>
217
+ )}
218
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  </div>
220
  </div>
221
  );
src/components/footer/footer.tsx DELETED
@@ -1,177 +0,0 @@
1
- import classNames from "classnames";
2
- import { FaMobileAlt, FaUserCircle } from "react-icons/fa";
3
- import { ChevronDown, LogOut, RefreshCcw, SparkleIcon } from "lucide-react";
4
- import { FaLaptopCode } from "react-icons/fa6";
5
- import { Auth, HtmlHistory } from "../../../utils/types";
6
- import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
7
- import {
8
- DropdownMenu,
9
- DropdownMenuContent,
10
- DropdownMenuGroup,
11
- DropdownMenuItem,
12
- DropdownMenuLabel,
13
- DropdownMenuSeparator,
14
- DropdownMenuTrigger,
15
- } from "../ui/dropdown-menu";
16
- import { Button } from "../ui/button";
17
- import { MdAdd } from "react-icons/md";
18
- import History from "../history/history";
19
-
20
- const DEVICES = [
21
- {
22
- name: "desktop",
23
- icon: FaLaptopCode,
24
- },
25
- {
26
- name: "mobile",
27
- icon: FaMobileAlt,
28
- },
29
- ];
30
-
31
- function Footer({
32
- onReset,
33
- auth,
34
- htmlHistory,
35
- setHtml,
36
- device,
37
- setDevice,
38
- iframeRef,
39
- }: {
40
- onReset: () => void;
41
- auth?: Auth;
42
- htmlHistory?: HtmlHistory[];
43
- device: "desktop" | "mobile";
44
- setHtml: (html: string) => void;
45
- iframeRef?: React.RefObject<HTMLIFrameElement | null>;
46
- setDevice: React.Dispatch<React.SetStateAction<"desktop" | "mobile">>;
47
- }) {
48
- const handleRefreshIframe = () => {
49
- if (iframeRef?.current) {
50
- const iframe = iframeRef.current;
51
- const content = iframe.srcdoc;
52
- iframe.srcdoc = "";
53
- setTimeout(() => {
54
- iframe.srcdoc = content;
55
- }, 10);
56
- }
57
- };
58
-
59
- return (
60
- <footer className="border-t bg-slate-200 border-slate-300 dark:bg-neutral-950 dark:border-neutral-800 px-3 py-2 flex items-center justify-between sticky bottom-0 z-20">
61
- <div className="flex items-center gap-2">
62
- {auth &&
63
- (auth?.isLocalUse ? (
64
- <>
65
- <div className="max-w-max bg-amber-500/10 rounded-full px-3 py-1 text-amber-500 border border-amber-500/20 text-sm font-semibold">
66
- Local Usage
67
- </div>
68
- </>
69
- ) : (
70
- <>
71
- <DropdownMenu>
72
- <DropdownMenuTrigger asChild>
73
- <p className="mr-3 text-xs lg:text-sm text-gray-300 flex items-center gap-1 cursor-pointer hover:brightness-110">
74
- <ChevronDown className="size-4" />
75
- <Avatar className="size-6 mr-1">
76
- <AvatarImage src={auth?.picture} alt="@shadcn" />
77
- <AvatarFallback className="text-sm">
78
- {auth?.preferred_username?.charAt(0).toUpperCase() ??
79
- "E"}
80
- </AvatarFallback>
81
- </Avatar>
82
- {auth?.preferred_username ?? "enzostvs"}
83
- </p>
84
- </DropdownMenuTrigger>
85
- <DropdownMenuContent className="w-56" align="start">
86
- <DropdownMenuLabel className="font-bold flex items-center gap-2 justify-start">
87
- <FaUserCircle className="" />
88
- My Account
89
- </DropdownMenuLabel>
90
- <DropdownMenuSeparator />
91
- <DropdownMenuGroup>
92
- <a
93
- href="https://huggingface.co/settings/billing"
94
- target="_blank"
95
- >
96
- <DropdownMenuItem>Usage Quota</DropdownMenuItem>
97
- </a>
98
- <a
99
- href={`https://huggingface.co/${auth?.preferred_username}`}
100
- target="_blank"
101
- >
102
- <DropdownMenuItem>Hugging Face profile</DropdownMenuItem>
103
- </a>
104
- </DropdownMenuGroup>
105
- <DropdownMenuSeparator />
106
- <DropdownMenuItem
107
- onClick={() => {
108
- if (confirm("Are you sure you want to log out?")) {
109
- // go to /auth/logout page
110
- window.location.href = "/auth/logout";
111
- }
112
- }}
113
- >
114
- <LogOut className="size-4 text-red-500" />
115
- Log out
116
- </DropdownMenuItem>
117
- </DropdownMenuContent>
118
- </DropdownMenu>
119
- </>
120
- ))}
121
- {auth && <p className="text-neutral-700">|</p>}
122
- <Button size="sm" variant="secondary" onClick={onReset}>
123
- <MdAdd className="text-sm" />
124
- <span>New Project</span>
125
- </Button>
126
- {htmlHistory && htmlHistory.length > 0 && (
127
- <>
128
- <p className="text-neutral-700">|</p>
129
- <History history={htmlHistory} setHtml={setHtml} />
130
- </>
131
- )}
132
- </div>
133
- <div className="flex justify-end items-center gap-2.5">
134
- <a
135
- href="https://huggingface.co/spaces/victor/deepsite-gallery"
136
- target="_blank"
137
- >
138
- <Button size="sm" variant="ghost">
139
- <SparkleIcon className="size-3.5" />
140
- <span className="max-lg:hidden">DeepSite Gallery</span>
141
- </Button>
142
- </a>
143
- <Button size="sm" variant="default" onClick={handleRefreshIframe}>
144
- <RefreshCcw className="size-3.5" />
145
- <span className="max-lg:hidden">Refresh Preview</span>
146
- </Button>
147
- <div className="flex items-center rounded-full p-0.5 bg-neutral-700/70 relative overflow-hidden z-0 max-lg:hidden gap-0.5">
148
- <div
149
- className={classNames(
150
- "absolute left-0.5 top-0.5 rounded-full bg-white size-7 -z-[1] transition-all duration-200",
151
- {
152
- "translate-x-[calc(100%+2px)]": device === "mobile",
153
- }
154
- )}
155
- />
156
- {DEVICES.map((deviceItem) => (
157
- <button
158
- key={deviceItem.name}
159
- className={classNames(
160
- "rounded-full text-neutral-300 size-7 flex items-center justify-center cursor-pointer",
161
- {
162
- "!text-black": device === deviceItem.name,
163
- "hover:bg-neutral-800": device !== deviceItem.name,
164
- }
165
- )}
166
- onClick={() => setDevice(deviceItem.name as "desktop" | "mobile")}
167
- >
168
- <deviceItem.icon className="text-sm" />
169
- </button>
170
- ))}
171
- </div>
172
- </div>
173
- </footer>
174
- );
175
- }
176
-
177
- export default Footer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/header/header.tsx CHANGED
@@ -1,68 +1,39 @@
1
  import { ReactNode } from "react";
2
- import { Eye, MessageCircleCode } from "lucide-react";
3
 
4
  import Logo from "@/assets/logo.svg";
5
 
6
- import { Button } from "./../../components/ui/button";
7
- import classNames from "classnames";
8
-
9
- const TABS = [
10
- {
11
- value: "chat",
12
- label: "Chat",
13
- icon: MessageCircleCode,
14
- },
15
- {
16
- value: "preview",
17
- label: "Preview",
18
- icon: Eye,
19
- },
20
- ];
21
-
22
  function Header({
23
- tab,
24
- onNewTab,
25
  children,
26
  }: {
27
- tab: string;
28
- onNewTab: (tab: string) => void;
29
  children?: ReactNode;
30
  }) {
31
  return (
32
- <header className="border-b bg-slate-200 border-slate-300 dark:bg-neutral-950 dark:border-neutral-800 px-3 lg:px-6 py-2 grid grid-cols-3 sticky top-0 z-20">
33
  <div className="flex items-center justify-start gap-3">
34
- <h1 className="text-neutral-900 dark:text-white text-lg lg:text-xl font-bold flex items-center justify-start">
35
  <img
36
  src={Logo}
37
  alt="DeepSite Logo"
38
- className="size-6 lg:size-8 mr-2 invert-100 dark:invert-0"
39
  />
40
- <p className="max-md:hidden flex items-center justify-start">
41
- DeepSite
42
- <span className="font-mono bg-gradient-to-br from-sky-500 to-emerald-500 text-neutral-950 rounded-full text-xs ml-2 px-1.5 py-0.5">
43
- {" "}
44
- v2
45
- </span>
46
- </p>
47
  </h1>
 
 
 
 
 
 
 
 
 
 
 
48
  </div>
49
- <div className="flex items-center justify-center gap-1">
50
- {TABS.map((item) => (
51
- <Button
52
- key={item.value}
53
- variant={tab === item.value ? "secondary" : "ghost"}
54
- className={classNames("", {
55
- "opacity-60": tab !== item.value,
56
- })}
57
- size="sm"
58
- onClick={() => onNewTab(item.value)}
59
- >
60
- <item.icon className="size-4" />
61
- <span className="hidden md:inline">{item.label}</span>
62
- </Button>
63
- ))}
64
- </div>
65
- <div className="flex items-center justify-end gap-3">{children}</div>
66
  </header>
67
  );
68
  }
 
1
  import { ReactNode } from "react";
2
+ import { MdAdd } from "react-icons/md";
3
 
4
  import Logo from "@/assets/logo.svg";
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  function Header({
7
+ onReset,
 
8
  children,
9
  }: {
10
+ onReset: () => void;
 
11
  children?: ReactNode;
12
  }) {
13
  return (
14
+ <header className="border-b border-gray-900 bg-gray-950 px-3 lg:px-6 py-2 flex justify-between items-center sticky top-0 z-20">
15
  <div className="flex items-center justify-start gap-3">
16
+ <h1 className="text-white text-lg lg:text-xl font-bold flex items-center justify-start">
17
  <img
18
  src={Logo}
19
  alt="DeepSite Logo"
20
+ className="size-6 lg:size-8 mr-2"
21
  />
22
+ DeepSite
 
 
 
 
 
 
23
  </h1>
24
+ <p className="text-gray-700 max-md:hidden">|</p>
25
+ <button
26
+ className="max-md:hidden relative cursor-pointer flex-none flex items-center justify-center rounded-md text-xs font-semibold leading-4 py-1.5 px-3 hover:bg-gray-700 text-gray-100 shadow-sm dark:shadow-highlight/20 bg-gray-800"
27
+ onClick={onReset}
28
+ >
29
+ <MdAdd className="mr-1 text-base" />
30
+ New
31
+ </button>
32
+ <p className="text-gray-500 text-sm max-md:hidden">
33
+ Imagine and Share in 1-Click
34
+ </p>
35
  </div>
36
+ {children}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  </header>
38
  );
39
  }
src/components/history/history.tsx DELETED
@@ -1,138 +0,0 @@
1
- import { HtmlHistory } from "../../../utils/types";
2
- import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
3
- import { Button } from "../ui/button";
4
-
5
- export default function History({
6
- history,
7
- setHtml,
8
- }: {
9
- history: HtmlHistory[];
10
- setHtml: (html: string) => void;
11
- }) {
12
- return (
13
- <Popover>
14
- <PopoverTrigger asChild>
15
- <Button variant="ghost" size="sm" className="max-lg:hidden">
16
- {history?.length} edit{history.length !== 1 ? "s" : ""}
17
- </Button>
18
- </PopoverTrigger>
19
- <PopoverContent
20
- className="!rounded-2xl !p-0 overflow-hidden !bg-neutral-900"
21
- align="start"
22
- >
23
- <header className="text-sm px-4 py-3 border-b gap-2 bg-neutral-950 border-neutral-800 font-semibold text-neutral-200">
24
- History
25
- </header>
26
- <main className="px-4 space-y-3">
27
- <ul className="max-h-[250px] overflow-y-auto">
28
- {history?.map((item, index) => (
29
- <li
30
- key={index}
31
- className="text-gray-300 text-xs py-2 border-b border-gray-800 last:border-0 flex items-center justify-between gap-2"
32
- >
33
- <div className="">
34
- <span className="line-clamp-1">{item.prompt}</span>
35
- <span className="text-gray-500 text-[10px]">
36
- {new Date(item.createdAt).toLocaleDateString("en-US", {
37
- month: "2-digit",
38
- day: "2-digit",
39
- year: "2-digit",
40
- }) +
41
- " " +
42
- new Date(item.createdAt).toLocaleTimeString("en-US", {
43
- hour: "2-digit",
44
- minute: "2-digit",
45
- second: "2-digit",
46
- hour12: false,
47
- })}
48
- </span>
49
- </div>
50
- <button
51
- className="bg-pink-500 text-white text-xs font-medium rounded-md px-2 py-1 transition-all duration-100 hover:bg-pink-600 cursor-pointer"
52
- onClick={() => {
53
- setHtml(item.html);
54
- }}
55
- >
56
- Select
57
- </button>
58
- </li>
59
- ))}
60
- </ul>
61
- </main>
62
- </PopoverContent>
63
- </Popover>
64
- // <div
65
- // className="relative"
66
- // onMouseEnter={() => setVisible(true)}
67
- // onMouseLeave={() => setVisible(false)}
68
- // >
69
- // <button
70
- // className={classNames(
71
- // "text-gray-400 hover:text-gray-300 cursor-pointer text-sm nderline flex items-center justify-start gap-1",
72
- // {
73
- // "!text-gray-300": visible,
74
- // }
75
- // )}
76
- // >
77
- // <IoTimeOutline />
78
- // {htmlHistory?.length} versions
79
- // </button>
80
- // <div
81
- // className={classNames(
82
- // "absolute bottom-0 left-0 min-w-sm w-full z-10 translate-y-full pt-2 transition-all duration-200",
83
- // {
84
- // "opacity-0 pointer-events-none": !visible,
85
- // }
86
- // )}
87
- // >
88
- // <div className="bg-gray-950 border border-gray-800 rounded-xl shadow-2xs p-4">
89
- // <p className="text-xs font-bold text-white">Version History</p>
90
- // <p className="text-gray-400 text-xs mt-1">
91
- // This is a list of the full history of what AI has done to
92
- // this.
93
- // </p>
94
- // <ul className="mt-2 max-h-[250px] overflow-y-auto">
95
- // {htmlHistory?.map((item, index) => (
96
- // <li
97
- // key={index}
98
- // className="text-gray-300 text-xs py-2 border-b border-gray-800 last:border-0 flex items-center justify-between gap-2"
99
- // >
100
- // <div className="">
101
- // <span className="line-clamp-1">{item.prompt}</span>
102
- // <span className="text-gray-500 text-[10px]">
103
- // {new Date(item.createdAt).toLocaleDateString(
104
- // "en-US",
105
- // {
106
- // month: "2-digit",
107
- // day: "2-digit",
108
- // year: "2-digit",
109
- // }
110
- // ) +
111
- // " " +
112
- // new Date(item.createdAt).toLocaleTimeString(
113
- // "en-US",
114
- // {
115
- // hour: "2-digit",
116
- // minute: "2-digit",
117
- // second: "2-digit",
118
- // hour12: false,
119
- // }
120
- // )}
121
- // </span>
122
- // </div>
123
- // <button
124
- // className="bg-pink-500 text-white text-xs font-medium rounded-md px-2 py-1 transition-all duration-100 hover:bg-pink-600 cursor-pointer"
125
- // onClick={() => {
126
- // setHtml(item.html);
127
- // }}
128
- // >
129
- // Select
130
- // </button>
131
- // </li>
132
- // ))}
133
- // </ul>
134
- // </div>
135
- // </div>
136
- // </div>
137
- );
138
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/invite-friends/invite-friends.tsx DELETED
@@ -1,78 +0,0 @@
1
- import { TiUserAdd } from "react-icons/ti";
2
- import { Link } from "lucide-react";
3
- import { FaXTwitter } from "react-icons/fa6";
4
-
5
- import { Button } from "../ui/button";
6
- import { Dialog, DialogContent, DialogTrigger } from "../ui/dialog";
7
- import { useCopyToClipboard } from "react-use";
8
- import { toast } from "sonner";
9
- export default function InviteFriends() {
10
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
- const [_, copyToClipboard] = useCopyToClipboard();
12
-
13
- return (
14
- <Dialog>
15
- <form>
16
- <DialogTrigger asChild>
17
- <Button
18
- size="iconXs"
19
- variant="outline"
20
- className="!border-neutral-600 !text-neutral-400 !hover:!border-neutral-500 hover:!text-neutral-300"
21
- >
22
- <TiUserAdd className="size-4" />
23
- </Button>
24
- </DialogTrigger>
25
- <DialogContent className="sm:max-w-lg lg:!p-8 !rounded-3xl !bg-white !border-neutral-100">
26
- <main>
27
- <div className="flex items-center justify-start -space-x-4 mb-5">
28
- <div className="size-11 rounded-full bg-pink-300 shadow-2xs flex items-center justify-center text-2xl">
29
- 😎
30
- </div>
31
- <div className="size-11 rounded-full bg-amber-300 shadow-2xs flex items-center justify-center text-2xl z-2">
32
- 😇
33
- </div>
34
- <div className="size-11 rounded-full bg-sky-300 shadow-2xs flex items-center justify-center text-2xl">
35
- 😜
36
- </div>
37
- </div>
38
- <p className="text-xl font-semibold text-neutral-950 max-w-[200px]">
39
- Invite your friends to join us!
40
- </p>
41
- <p className="text-sm text-neutral-500 mt-2 max-w-sm">
42
- Support us and share the love and let them know about our awesome
43
- platform.
44
- </p>
45
- <div className="mt-4 space-x-3.5">
46
- <a
47
- href="https://x.com/intent/post?url=https://enzostvs-deepsite.hf.space/&text=Checkout%20this%20awesome%20Ai%20Tool!%20Vibe%20coding%20has%20never%20been%20so%20easy✨"
48
- target="_blank"
49
- rel="noopener noreferrer"
50
- >
51
- <Button
52
- variant="ghostWhite"
53
- size="sm"
54
- className="!text-neutral-700"
55
- >
56
- <FaXTwitter className="size-4" />
57
- Share on
58
- </Button>
59
- </a>
60
- <Button
61
- variant="ghostWhite"
62
- size="sm"
63
- className="!text-neutral-700"
64
- onClick={() => {
65
- copyToClipboard("https://enzostvs-deepsite.hf.space/");
66
- toast.success("Invite link copied to clipboard!");
67
- }}
68
- >
69
- <Link className="size-4" />
70
- Copy Invite Link
71
- </Button>
72
- </div>
73
- </main>
74
- </DialogContent>
75
- </form>
76
- </Dialog>
77
- );
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/load-button/load-button.tsx CHANGED
@@ -1,13 +1,10 @@
1
  import classNames from "classnames";
2
  import { useState } from "react";
3
- import { toast } from "sonner";
4
 
5
  import SpaceIcon from "@/assets/space.svg";
6
  import Loading from "../loading/loading";
7
  import { Auth } from "../../../utils/types";
8
- import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
9
- import { Input } from "../ui/input";
10
- import { Button } from "../ui/button";
11
 
12
  function LoadButton({
13
  auth,
@@ -20,6 +17,7 @@ function LoadButton({
20
  }) {
21
  const [open, setOpen] = useState(false);
22
  const [loading, setLoading] = useState(false);
 
23
  const [url, setUrl] = useState<string | undefined>(undefined);
24
 
25
  const loadSpace = async () => {
@@ -40,10 +38,12 @@ function LoadButton({
40
  setOpen(false);
41
  } else {
42
  toast.error(data.message);
 
43
  }
44
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
  } catch (error: any) {
46
  toast.error(error.message);
 
47
  }
48
  setLoading(false);
49
  };
@@ -54,73 +54,81 @@ function LoadButton({
54
  "border-r border-gray-700 pr-5": auth,
55
  })}
56
  >
57
- <Popover>
58
- <PopoverTrigger asChild>
59
- <p
60
- className="underline hover:text-white cursor-pointer text-xs lg:text-sm text-gray-300"
61
- onClick={() => setOpen(!open)}
62
- >
63
- Load Space
64
- </p>
65
- </PopoverTrigger>
66
- <PopoverContent
67
- className="!rounded-2xl p-0 overflow-hidden !bg-neutral-900"
68
- align="end"
69
- >
70
- <header className="flex items-center text-sm px-4 py-3 border-b gap-2 bg-neutral-950 border-neutral-800 font-semibold text-neutral-200">
71
- <span className="text-xs bg-pink-500 text-white rounded-full pl-1.5 pr-2.5 py-0.5 flex items-center justify-start gap-1.5">
 
 
 
 
 
 
 
 
 
 
 
72
  <img src={SpaceIcon} alt="Space Icon" className="size-4" />
73
  Space
74
  </span>
75
  Load Project
76
  </header>
77
  <main className="px-4 pt-3 pb-4 space-y-3">
78
- <p className="text-sm text-neutral-300 bg-neutral-300/15 border border-neutral-300/15 rounded-md px-3 py-2">
79
  Load an existing DeepSite Space to continue working on it.
80
  </p>
81
  <label className="block">
82
- <p className="text-muted-foreground text-sm mb-1.5">Space URL</p>
83
- <Input
 
 
84
  type="text"
85
  value={url}
86
- onChange={(e) => setUrl(e.target.value)}
87
  placeholder="https://huggingface.co/spaces/username/space-name"
 
 
88
  onBlur={(e) => {
89
- if (!e.target.value) {
90
- return setUrl(undefined);
91
- }
92
- if (
93
- !e.target.value.startsWith(
94
- "https://huggingface.co/spaces/"
95
- ) &&
96
- !e.target.value.includes("/")
97
- ) {
98
- toast.error("Please enter a valid Hugging Face Space URL.");
99
- return;
100
- }
101
  const pathParts = e.target.value.split("/");
102
  setUrl(
103
  `${pathParts[pathParts.length - 2]}/${
104
  pathParts[pathParts.length - 1]
105
  }`
106
  );
 
107
  }}
108
  />
109
  </label>
 
 
 
 
 
110
  <div className="pt-2 text-right">
111
- <Button
112
- size="sm"
113
- variant="default"
114
- disabled={loading || !url}
115
  onClick={loadSpace}
116
  >
117
- Load space
118
  {loading && <Loading />}
119
- </Button>
120
  </div>
121
  </main>
122
- </PopoverContent>
123
- </Popover>
124
  </div>
125
  );
126
  }
 
1
  import classNames from "classnames";
2
  import { useState } from "react";
3
+ import { toast } from "react-toastify";
4
 
5
  import SpaceIcon from "@/assets/space.svg";
6
  import Loading from "../loading/loading";
7
  import { Auth } from "../../../utils/types";
 
 
 
8
 
9
  function LoadButton({
10
  auth,
 
17
  }) {
18
  const [open, setOpen] = useState(false);
19
  const [loading, setLoading] = useState(false);
20
+ const [error, setError] = useState(false);
21
  const [url, setUrl] = useState<string | undefined>(undefined);
22
 
23
  const loadSpace = async () => {
 
38
  setOpen(false);
39
  } else {
40
  toast.error(data.message);
41
+ setError(data.message);
42
  }
43
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
  } catch (error: any) {
45
  toast.error(error.message);
46
+ setError(error.message);
47
  }
48
  setLoading(false);
49
  };
 
54
  "border-r border-gray-700 pr-5": auth,
55
  })}
56
  >
57
+ <p
58
+ className="underline hover:text-white cursor-pointer text-xs lg:text-sm text-gray-300"
59
+ onClick={() => setOpen(!open)}
60
+ >
61
+ Load Space
62
+ </p>
63
+ <div
64
+ className={classNames(
65
+ "h-screen w-screen bg-black/20 fixed left-0 top-0 z-10",
66
+ {
67
+ "opacity-0 pointer-events-none": !open,
68
+ }
69
+ )}
70
+ onClick={() => setOpen(false)}
71
+ ></div>
72
+ <div
73
+ className={classNames(
74
+ "absolute top-[calc(100%+8px)] right-2 z-10 w-80 bg-white border border-gray-200 rounded-lg shadow-lg transition-all duration-75 overflow-hidden",
75
+ {
76
+ "opacity-0 pointer-events-none": !open,
77
+ }
78
+ )}
79
+ >
80
+ <>
81
+ <header className="flex items-center text-sm px-4 py-2 border-b border-gray-200 gap-2 bg-gray-100 font-semibold text-gray-700">
82
+ <span className="text-xs bg-pink-500/10 text-pink-500 rounded-full pl-1.5 pr-2.5 py-0.5 flex items-center justify-start gap-1.5">
83
  <img src={SpaceIcon} alt="Space Icon" className="size-4" />
84
  Space
85
  </span>
86
  Load Project
87
  </header>
88
  <main className="px-4 pt-3 pb-4 space-y-3">
89
+ <p className="text-sm text-pink-600 bg-pink-100 rounded-md px-3 py-2">
90
  Load an existing DeepSite Space to continue working on it.
91
  </p>
92
  <label className="block">
93
+ <p className="text-gray-600 text-sm font-medium mb-1.5">
94
+ Space URL
95
+ </p>
96
+ <input
97
  type="text"
98
  value={url}
99
+ className="mr-2 border rounded-md px-3 py-1.5 border-gray-300 w-full text-sm"
100
  placeholder="https://huggingface.co/spaces/username/space-name"
101
+ onChange={(e) => setUrl(e.target.value)}
102
+ onFocus={() => setError(false)}
103
  onBlur={(e) => {
 
 
 
 
 
 
 
 
 
 
 
 
104
  const pathParts = e.target.value.split("/");
105
  setUrl(
106
  `${pathParts[pathParts.length - 2]}/${
107
  pathParts[pathParts.length - 1]
108
  }`
109
  );
110
+ setError(false);
111
  }}
112
  />
113
  </label>
114
+ {error && (
115
+ <p className="text-red-500 text-xs bg-red-500/10 rounded-md p-2 break-all">
116
+ {error}
117
+ </p>
118
+ )}
119
  <div className="pt-2 text-right">
120
+ <button
121
+ disabled={error || loading || !url}
122
+ className="relative rounded-full bg-black px-5 py-2 text-white font-semibold text-xs hover:bg-black/90 transition-all duration-100 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed disabled:hover:bg-gray-300"
 
123
  onClick={loadSpace}
124
  >
125
+ Load Space
126
  {loading && <Loading />}
127
+ </button>
128
  </div>
129
  </main>
130
+ </>
131
+ </div>
132
  </div>
133
  );
134
  }
src/components/loading/loading.tsx CHANGED
@@ -1,21 +1,8 @@
1
- import classNames from "classnames";
2
-
3
- function Loading({
4
- overlay = true,
5
- className,
6
- }: {
7
- overlay?: boolean;
8
- className?: string;
9
- }) {
10
  return (
11
- <div
12
- className={classNames("", {
13
- "absolute left-0 top-0 h-full w-full flex items-center justify-center z-20 bg-black/50 rounded-full":
14
- overlay,
15
- })}
16
- >
17
  <svg
18
- className={`size-5 animate-spin text-white ${className}`}
19
  xmlns="http://www.w3.org/2000/svg"
20
  fill="none"
21
  viewBox="0 0 24 24"
 
1
+ function Loading() {
 
 
 
 
 
 
 
 
2
  return (
3
+ <div className="absolute left-0 top-0 h-full w-full flex items-center justify-center bg-white/30 z-20">
 
 
 
 
 
4
  <svg
5
+ className="size-5 animate-spin text-white"
6
  xmlns="http://www.w3.org/2000/svg"
7
  fill="none"
8
  viewBox="0 0 24 24"
src/components/login/login.tsx CHANGED
@@ -23,17 +23,17 @@ function Login({
23
 
24
  return (
25
  <>
26
- <header className="flex items-center text-sm px-4 py-3 border-b gap-2 bg-neutral-950 border-neutral-800 font-semibold text-neutral-200">
27
- <span className="text-xs bg-red-500 text-white rounded-full px-1.5 py-0.5 flex items-center justify-start gap-1.5">
28
  REQUIRED
29
  </span>
30
  Login with Hugging Face
31
  </header>
32
  <main className="px-4 py-4 space-y-3">
33
  {children}
34
- <button onClick={handleClick} className="cursor-pointer">
35
  <img
36
- src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-lg.svg"
37
  alt="Sign in with Hugging Face"
38
  className="mx-auto"
39
  />
 
23
 
24
  return (
25
  <>
26
+ <header className="flex items-center text-sm px-4 py-2 border-b border-gray-200 gap-2 bg-gray-100 font-semibold text-gray-700">
27
+ <span className="text-xs bg-red-500/10 text-red-500 rounded-full pl-1.5 pr-2.5 py-0.5 flex items-center justify-start gap-1.5">
28
  REQUIRED
29
  </span>
30
  Login with Hugging Face
31
  </header>
32
  <main className="px-4 py-4 space-y-3">
33
  {children}
34
+ <button onClick={handleClick}>
35
  <img
36
+ src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-lg-dark.svg"
37
  alt="Sign in with Hugging Face"
38
  className="mx-auto"
39
  />
src/components/magicui/grid-pattern.tsx DELETED
@@ -1,69 +0,0 @@
1
- import { useId } from "react";
2
- import { cn } from "../../lib/utils";
3
-
4
- interface GridPatternProps extends React.SVGProps<SVGSVGElement> {
5
- width?: number;
6
- height?: number;
7
- x?: number;
8
- y?: number;
9
- squares?: Array<[x: number, y: number]>;
10
- strokeDasharray?: string;
11
- className?: string;
12
- [key: string]: unknown;
13
- }
14
-
15
- export function GridPattern({
16
- width = 40,
17
- height = 40,
18
- x = -1,
19
- y = -1,
20
- strokeDasharray = "0",
21
- squares,
22
- className,
23
- ...props
24
- }: GridPatternProps) {
25
- const id = useId();
26
-
27
- return (
28
- <svg
29
- aria-hidden="true"
30
- className={cn(
31
- "pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-neutral-700 -z-[1]",
32
- className
33
- )}
34
- {...props}
35
- >
36
- <defs>
37
- <pattern
38
- id={id}
39
- width={width}
40
- height={height}
41
- patternUnits="userSpaceOnUse"
42
- x={x}
43
- y={y}
44
- >
45
- <path
46
- d={`M.5 ${height}V.5H${width}`}
47
- fill="none"
48
- strokeDasharray={strokeDasharray}
49
- />
50
- </pattern>
51
- </defs>
52
- <rect width="100%" height="100%" strokeWidth={0} fill={`url(#${id})`} />
53
- {squares && (
54
- <svg x={x} y={y} className="overflow-visible">
55
- {squares.map(([x, y]) => (
56
- <rect
57
- strokeWidth="0"
58
- key={`${x}-${y}`}
59
- width={width - 1}
60
- height={height - 1}
61
- x={x * width + 1}
62
- y={y * height + 1}
63
- />
64
- ))}
65
- </svg>
66
- )}
67
- </svg>
68
- );
69
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/preview/preview.tsx CHANGED
@@ -1,77 +1,83 @@
1
  import classNames from "classnames";
2
-
3
- import { toast } from "sonner";
4
- import { GridPattern } from "./../magicui/grid-pattern";
5
- import { cn } from "../../lib/utils";
 
6
 
7
  function Preview({
8
  html,
9
  isResizing,
10
  isAiWorking,
 
11
  ref,
12
- device,
13
- currentTab,
14
- iframeRef,
15
  }: {
16
  html: string;
17
  isResizing: boolean;
18
  isAiWorking: boolean;
 
19
  ref: React.RefObject<HTMLDivElement | null>;
20
- iframeRef?: React.RefObject<HTMLIFrameElement | null>;
21
- device: "desktop" | "mobile";
22
- currentTab: string;
23
  }) {
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  return (
25
  <div
26
  ref={ref}
27
- className={classNames(
28
- "w-full border-l border-gray-900 h-full relative z-0 flex items-center justify-center",
29
- {
30
- "lg:p-4": currentTab !== "preview",
31
- }
32
- )}
33
  onClick={(e) => {
34
  if (isAiWorking) {
35
  e.preventDefault();
36
  e.stopPropagation();
37
- toast.warning("Please wait for the AI to finish working.");
38
  }
39
  }}
40
  >
41
- <GridPattern
42
- x={-1}
43
- y={-1}
44
- strokeDasharray={"4 2"}
45
- className={cn(
46
- "[mask-image:radial-gradient(900px_circle_at_center,white,transparent)]"
47
- )}
48
- />
49
  <iframe
50
- id="preview-iframe"
51
  ref={iframeRef}
52
  title="output"
53
- className={classNames(
54
- "w-full select-none transition-all duration-200 bg-black max-lg:h-full",
55
- {
56
- "pointer-events-none": isResizing || isAiWorking,
57
- "lg:max-w-md lg:mx-auto lg:h-[80dvh] lg:!rounded-[42px] lg:border-[8px] lg:border-neutral-700 lg:shadow-2xl":
58
- device === "mobile",
59
- "h-full": device === "desktop",
60
- "lg:border-[8px] lg:border-neutral-700 lg:shadow-2xl lg:rounded-[44px]":
61
- currentTab !== "preview" && device === "desktop",
62
- }
63
- )}
64
  srcDoc={html}
65
- onLoad={() => {
66
- if (iframeRef?.current?.contentWindow?.document?.body) {
67
- iframeRef.current.contentWindow.document.body.scrollIntoView({
68
- block: isAiWorking ? "end" : "start",
69
- inline: "nearest",
70
- behavior: isAiWorking ? "instant" : "smooth",
71
- });
72
- }
73
- }}
74
  />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  </div>
76
  );
77
  }
 
1
  import classNames from "classnames";
2
+ import { useRef } from "react";
3
+ import { TbReload } from "react-icons/tb";
4
+ import { toast } from "react-toastify";
5
+ import { FaLaptopCode } from "react-icons/fa6";
6
+ import { defaultHTML } from "../../../utils/consts";
7
 
8
  function Preview({
9
  html,
10
  isResizing,
11
  isAiWorking,
12
+ setView,
13
  ref,
 
 
 
14
  }: {
15
  html: string;
16
  isResizing: boolean;
17
  isAiWorking: boolean;
18
+ setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
19
  ref: React.RefObject<HTMLDivElement | null>;
 
 
 
20
  }) {
21
+ const iframeRef = useRef<HTMLIFrameElement | null>(null);
22
+
23
+ const handleRefreshIframe = () => {
24
+ if (iframeRef.current) {
25
+ const iframe = iframeRef.current;
26
+ const content = iframe.srcdoc;
27
+ iframe.srcdoc = "";
28
+ setTimeout(() => {
29
+ iframe.srcdoc = content;
30
+ }, 10);
31
+ }
32
+ };
33
+
34
  return (
35
  <div
36
  ref={ref}
37
+ className="w-full border-l border-gray-900 bg-white h-[calc(100dvh-49px)] lg:h-[calc(100dvh-53px)] relative"
 
 
 
 
 
38
  onClick={(e) => {
39
  if (isAiWorking) {
40
  e.preventDefault();
41
  e.stopPropagation();
42
+ toast.warn("Please wait for the AI to finish working.");
43
  }
44
  }}
45
  >
 
 
 
 
 
 
 
 
46
  <iframe
 
47
  ref={iframeRef}
48
  title="output"
49
+ className={classNames("w-full h-full select-none", {
50
+ "pointer-events-none": isResizing || isAiWorking,
51
+ })}
 
 
 
 
 
 
 
 
52
  srcDoc={html}
 
 
 
 
 
 
 
 
 
53
  />
54
+ <div className="flex items-center justify-start gap-3 absolute bottom-3 lg:bottom-5 max-lg:left-3 lg:right-5">
55
+ <button
56
+ className="lg:hidden bg-gray-950 shadow-md text-white text-xs lg:text-sm font-medium py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-900 hover:brightness-150 transition-all duration-100 cursor-pointer"
57
+ onClick={() => setView("editor")}
58
+ >
59
+ <FaLaptopCode className="text-sm" />
60
+ Hide preview
61
+ </button>
62
+ {html === defaultHTML && (
63
+ <a
64
+ href="https://huggingface.co/spaces/victor/deepsite-gallery"
65
+ target="_blank"
66
+ className="bg-gray-200 text-gray-950 text-xs lg:text-sm font-medium py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-200 hover:bg-gray-300 transition-all duration-100 cursor-pointer"
67
+ >
68
+ 🖼️ <span>DeepSite Gallery</span>
69
+ </a>
70
+ )}
71
+ {!isAiWorking && (
72
+ <button
73
+ className="bg-white lg:bg-gray-950 shadow-md text-gray-950 lg:text-white text-xs lg:text-sm font-medium py-2 px-3 lg:px-4 rounded-lg flex items-center gap-2 border border-gray-100 lg:border-gray-900 hover:brightness-150 transition-all duration-100 cursor-pointer"
74
+ onClick={handleRefreshIframe}
75
+ >
76
+ <TbReload className="text-sm" />
77
+ Refresh Preview
78
+ </button>
79
+ )}
80
+ </div>
81
  </div>
82
  );
83
  }
src/components/pro-modal/pro-modal.tsx CHANGED
@@ -1,8 +1,6 @@
 
1
  import { useLocalStorage } from "react-use";
2
  import { defaultHTML } from "../../../utils/consts";
3
- import { Dialog, DialogContent } from "../ui/dialog";
4
- import { Button } from "../ui/button";
5
- import { CheckCheck } from "lucide-react";
6
 
7
  function ProModal({
8
  open,
@@ -22,73 +20,51 @@ function ProModal({
22
  };
23
 
24
  return (
25
- <Dialog open={open} onOpenChange={onClose}>
26
- <DialogContent className="sm:max-w-lg lg:!p-8 !rounded-3xl !bg-white !border-neutral-100">
27
- <main className="flex flex-col items-start text-left relative pt-2">
28
- <div className="flex items-center justify-start -space-x-4 mb-5">
29
- <div className="size-14 rounded-full bg-pink-200 shadow-2xs flex items-center justify-center text-3xl opacity-50">
30
- 🚀
31
- </div>
32
- <div className="size-16 rounded-full bg-amber-200 shadow-2xl flex items-center justify-center text-4xl z-2">
33
- 🤩
34
- </div>
35
- <div className="size-14 rounded-full bg-sky-200 shadow-2xs flex items-center justify-center text-3xl opacity-50">
36
- 🥳
37
- </div>
38
- </div>
39
- <p className="text-2xl font-bold text-neutral-950">
40
- Only 9$ for unlimited access!
 
 
 
 
 
 
 
 
 
 
 
 
41
  </p>
42
- <p className="text-neutral-500 text-base mt-2 max-w-sm">
43
- It seems like you have reached the monthly free limit of DeepSite.
 
44
  </p>
45
- <hr className="bg-neutral-200 w-full max-w-[150px] my-6" />
46
- <p className="text-lg mt-3 text-neutral-900 font-semibold">
47
- Upgrade to a <ProTag className="mx-1" /> Account, and unlock:
48
- </p>
49
- <ul className="mt-3 space-y-1 text-neutral-500">
50
- <li className="text-sm space-x-2 flex items-center justify-start gap-2">
51
- <CheckCheck className="text-emerald-500 size-4" />
52
- DeepSite unlimited access to all Inference Providers
53
- </li>
54
- <li className="text-sm space-x-2 flex items-center justify-start gap-2">
55
- <CheckCheck className="text-emerald-500 size-4" />
56
- Get highest priority and 8x more quota on Spaces ZeroGPU
57
- </li>
58
- <li className="text-sm space-x-2 flex items-center justify-start gap-2">
59
- <CheckCheck className="text-emerald-500 size-4" />
60
- Activate Dataset Viewer on private datasets
61
- </li>
62
- <li className="text-sm space-x-2 flex items-center justify-start gap-2">
63
- <CheckCheck className="text-emerald-500 size-4" />
64
- Get exclusive early access to new features and updates
65
- </li>
66
- <li className="text-sm space-x-2 flex items-center justify-start gap-2">
67
- <CheckCheck className="text-emerald-500 size-4" />
68
- Get free credits across all Inference Providers
69
- </li>
70
- <li className="text-sm text-neutral-500 space-x-2 flex items-center justify-start gap-2 mt-3">
71
- ... and much more!
72
- </li>
73
- </ul>
74
- <Button
75
- variant="gray"
76
- size="lg"
77
- className="w-full !text-base !h-11 mt-8"
78
  onClick={handleProClick}
79
  >
80
  Subscribe to PRO ($9/month)
81
- </Button>
82
  </main>
83
- </DialogContent>
84
- </Dialog>
85
  );
86
  }
87
- const ProTag = ({ className }: { className?: string }) => (
88
- <span
89
- className={`${className} bg-linear-to-br shadow-green-500/10 dark:shadow-green-500/20 inline-block -skew-x-12 border border-gray-200 from-pink-300 via-green-200 to-yellow-200 text-xs font-bold text-black shadow-lg rounded-md px-2.5 py-0.5`}
90
- >
91
- PRO
92
- </span>
93
- );
94
  export default ProModal;
 
1
+ import classNames from "classnames";
2
  import { useLocalStorage } from "react-use";
3
  import { defaultHTML } from "../../../utils/consts";
 
 
 
4
 
5
  function ProModal({
6
  open,
 
20
  };
21
 
22
  return (
23
+ <>
24
+ <div
25
+ className={classNames(
26
+ "h-screen w-screen bg-black/20 fixed left-0 top-0 z-40",
27
+ {
28
+ "opacity-0 pointer-events-none": !open,
29
+ }
30
+ )}
31
+ onClick={() => onClose(false)}
32
+ ></div>
33
+ <div
34
+ className={classNames(
35
+ "absolute top-0 -translate-y-[calc(100%+16px)] right-0 z-40 w-96 bg-white border border-gray-200 rounded-lg shadow-lg transition-all duration-75 overflow-hidden",
36
+ {
37
+ "opacity-0 pointer-events-none": !open,
38
+ }
39
+ )}
40
+ >
41
+ <header className="flex items-center text-sm px-4 py-2 border-b border-gray-200 gap-2 bg-gray-100 font-semibold text-gray-700">
42
+ <span className="bg-linear-to-br shadow-green-500/10 dark:shadow-green-500/20 inline-block -skew-x-12 border border-gray-200 from-pink-300 via-green-200 to-yellow-200 text-xs font-bold text-black shadow-lg dark:from-pink-500 dark:via-green-500 dark:to-yellow-500 dark:text-black rounded-lg px-2.5 py-0.5 ">
43
+ PRO
44
+ </span>
45
+ Your free inference quota is exhausted
46
+ </header>
47
+ <main className="px-4 pt-3 pb-4">
48
+ <p className="text-gray-950 text-sm font-semibold flex items-center justify-between">
49
+ Upgrade to a PRO account to activate Inference Providers and
50
+ continue using DeepSite.
51
  </p>
52
+ <p className="text-sm text-pretty text-gray-500 mt-2">
53
+ It also unlocks thousands of Space apps powered by ZeroGPU for 3d,
54
+ audio, music, video and more!
55
  </p>
56
+ <a
57
+ href="https://huggingface.co/subscribe/pro"
58
+ target="_blank"
59
+ className="mt-4 bg-black text-white cursor-pointer rounded-full py-2 px-3 text-sm font-medium w-full block text-center hover:bg-gray-800 transition duration-200 ease-in-out"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  onClick={handleProClick}
61
  >
62
  Subscribe to PRO ($9/month)
63
+ </a>
64
  </main>
65
+ </div>
66
+ </>
67
  );
68
  }
69
+
 
 
 
 
 
 
70
  export default ProModal;
src/components/re-imagine/re-imagine.tsx DELETED
@@ -1,136 +0,0 @@
1
- import { useState } from "react";
2
- import { Paintbrush } from "lucide-react";
3
- import { toast } from "sonner";
4
-
5
- import { Button } from "../ui/button";
6
- import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
7
- import { Input } from "../ui/input";
8
- import Loading from "../loading/loading";
9
-
10
- export default function ReImagine({
11
- onRedesign,
12
- }: {
13
- onRedesign: (md: string) => void;
14
- }) {
15
- const [url, setUrl] = useState<string>("");
16
- const [open, setOpen] = useState(false);
17
- const [isLoading, setIsLoading] = useState(false);
18
-
19
- const checkIfUrlIsValid = (url: string) => {
20
- const urlPattern = new RegExp(
21
- /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/,
22
- "i"
23
- );
24
- return urlPattern.test(url);
25
- };
26
-
27
- const handleClick = async () => {
28
- if (!url) {
29
- toast.error("Please enter a URL.");
30
- return;
31
- }
32
- if (!checkIfUrlIsValid(url)) {
33
- toast.error("Please enter a valid URL.");
34
- return;
35
- }
36
- // Here you would typically handle the re-design logic
37
- setIsLoading(true);
38
- const request = await fetch("/api/re-design", {
39
- method: "POST",
40
- body: JSON.stringify({ url }),
41
- headers: {
42
- "Content-Type": "application/json",
43
- },
44
- });
45
- const response = await request.json();
46
- if (response.ok) {
47
- setOpen(false);
48
- setUrl("");
49
- onRedesign(response.markdown);
50
- toast.success("DeepSite is re-designing your site! Let him cook... 🔥");
51
- } else {
52
- toast.error(response.message || "Failed to re-design the site.");
53
- }
54
- setIsLoading(false);
55
- };
56
-
57
- return (
58
- <Popover open={open} onOpenChange={setOpen}>
59
- <form>
60
- <PopoverTrigger asChild>
61
- <Button
62
- size="iconXs"
63
- variant="outline"
64
- className="!border-neutral-600 !text-neutral-400 !hover:!border-neutral-500 hover:!text-neutral-300"
65
- >
66
- <Paintbrush className="size-4" />
67
- </Button>
68
- </PopoverTrigger>
69
- <PopoverContent
70
- align="start"
71
- className="!rounded-2xl !p-0 !bg-white !border-neutral-100 min-w-xs text-center overflow-hidden"
72
- >
73
- <header className="bg-neutral-50 p-6 border-b border-neutral-200/60">
74
- <div className="flex items-center justify-center -space-x-4 mb-3">
75
- <div className="size-9 rounded-full bg-pink-200 shadow-2xs flex items-center justify-center text-xl opacity-50">
76
- 🎨
77
- </div>
78
- <div className="size-11 rounded-full bg-amber-200 shadow-2xl flex items-center justify-center text-2xl z-2">
79
- 🥳
80
- </div>
81
- <div className="size-9 rounded-full bg-sky-200 shadow-2xs flex items-center justify-center text-xl opacity-50">
82
- 💎
83
- </div>
84
- </div>
85
- <p className="text-xl font-semibold text-neutral-950">
86
- Redesign your Site!
87
- </p>
88
- <p className="text-sm text-neutral-500 mt-1.5">
89
- Try our new Redesign feature to give your site a fresh look.
90
- </p>
91
- </header>
92
- <main className="space-y-4 p-6">
93
- <div>
94
- <p className="text-sm text-neutral-700 mb-2">
95
- Enter your website URL to get started:
96
- </p>
97
- <Input
98
- type="text"
99
- placeholder="https://example.com"
100
- value={url}
101
- onChange={(e) => setUrl(e.target.value)}
102
- onBlur={(e) => {
103
- const inputUrl = e.target.value.trim();
104
- if (!inputUrl) {
105
- setUrl("");
106
- return;
107
- }
108
- if (!checkIfUrlIsValid(inputUrl)) {
109
- toast.error("Please enter a valid URL.");
110
- return;
111
- }
112
- setUrl(inputUrl);
113
- }}
114
- className="!bg-white !border-neutral-300 !text-neutral-800 !placeholder:text-neutral-400 selection:!bg-blue-100"
115
- />
116
- </div>
117
- <div>
118
- <p className="text-sm text-neutral-700 mb-2">
119
- Then, let's redesign it!
120
- </p>
121
- <Button
122
- variant="gray"
123
- onClick={handleClick}
124
- className="relative w-full"
125
- disabled={isLoading}
126
- >
127
- Redesign <Paintbrush className="size-4" />
128
- {isLoading && <Loading className="ml-2 size-4 animate-spin" />}
129
- </Button>
130
- </div>
131
- </main>
132
- </PopoverContent>
133
- </form>
134
- </Popover>
135
- );
136
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/settings/settings.tsx CHANGED
@@ -1,195 +1,136 @@
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
  import classNames from "classnames";
3
- import { PiGearSixFill } from "react-icons/pi";
4
- import { RiCheckboxCircleFill } from "react-icons/ri";
5
 
6
- import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
7
  // @ts-expect-error not needed
8
- import { PROVIDERS, MODELS } from "./../../../utils/providers";
9
- import { Button } from "../ui/button";
10
- import {
11
- Select,
12
- SelectContent,
13
- SelectGroup,
14
- SelectItem,
15
- SelectLabel,
16
- SelectTrigger,
17
- SelectValue,
18
- } from "../ui/select";
19
- import { useMemo } from "react";
20
- import { useUpdateEffect } from "react-use";
21
 
22
  function Settings({
23
  open,
24
  onClose,
25
  provider,
26
- model,
27
  error,
28
  onChange,
29
- onModelChange,
30
  }: {
31
  open: boolean;
32
  provider: string;
33
- model: string;
34
  error?: string;
35
  onClose: React.Dispatch<React.SetStateAction<boolean>>;
36
  onChange: (provider: string) => void;
37
- onModelChange: (model: string) => void;
38
  }) {
39
- const modelAvailableProviders = useMemo(() => {
40
- const availableProviders = MODELS.find(
41
- (m: { value: string }) => m.value === model
42
- )?.providers;
43
- if (!availableProviders) return Object.keys(PROVIDERS);
44
- return Object.keys(PROVIDERS).filter((id) =>
45
- availableProviders.includes(id)
46
- );
47
- }, [model]);
48
-
49
- useUpdateEffect(() => {
50
- if (provider !== "auto" && !modelAvailableProviders.includes(provider)) {
51
- onChange("auto");
52
- }
53
- }, [model, provider]);
54
-
55
  return (
56
  <div className="">
57
- <Popover open={open} onOpenChange={onClose}>
58
- <PopoverTrigger asChild>
59
- <Button variant="gray" size="sm">
60
- <PiGearSixFill className="size-4" />
61
- Settings
62
- </Button>
63
- </PopoverTrigger>
64
- <PopoverContent
65
- className="!rounded-2xl p-0 !w-96 overflow-hidden !bg-neutral-900"
66
- align="center"
67
- >
68
- <header className="flex items-center text-sm px-4 py-3 border-b gap-2 bg-neutral-950 border-neutral-800 font-semibold text-neutral-200">
69
- {/* <span className="text-xs bg-blue-500 text-white rounded-full px-1.5 py-0.5">
70
- Provider
71
- </span> */}
72
- Customize Settings
73
- </header>
74
- <main className="px-4 pt-5 pb-6 space-y-5">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  <a
76
  href="https://huggingface.co/spaces/enzostvs/deepsite/discussions/74"
77
  target="_blank"
78
- className="w-full flex items-center justify-between text-neutral-300 bg-neutral-300/15 border border-neutral-300/15 pl-4 p-1.5 rounded-full text-sm font-medium hover:brightness-95"
79
  >
80
  How to use it locally?
81
- <Button size="xs">See guide</Button>
 
 
82
  </a>
83
- {error !== "" && (
84
- <p className="text-red-500 text-sm font-medium mb-2 flex items-center justify-between bg-red-500/10 p-2 rounded-md">
85
- {error}
86
  </p>
87
- )}
88
- <label className="block">
89
- <p className="text-neutral-300 text-sm mb-2.5">
90
- Choose a DeepSeek model
91
- </p>
92
- <Select defaultValue={model} onValueChange={onModelChange}>
93
- <SelectTrigger className="w-full">
94
- <SelectValue placeholder="Select a DeepSeek model" />
95
- </SelectTrigger>
96
- <SelectContent>
97
- <SelectGroup>
98
- <SelectLabel>DeepSeek models</SelectLabel>
99
- {MODELS.map(
100
- ({
101
- value,
102
- label,
103
- isNew = false,
104
- }: {
105
- value: string;
106
- label: string;
107
- isNew?: boolean;
108
- }) => (
109
- <SelectItem key={value} value={value} className="">
110
- {label}
111
- {isNew && (
112
- <span className="text-xs bg-gradient-to-br from-sky-400 to-sky-600 text-white rounded-full px-1.5 py-0.5">
113
- New
114
- </span>
115
- )}
116
- </SelectItem>
117
- )
118
- )}
119
- </SelectGroup>
120
- </SelectContent>
121
- </Select>
122
- </label>
123
- <div className="flex flex-col gap-3">
124
- <div className="flex items-center justify-between">
125
- <div>
126
- <p className="text-neutral-300 text-sm mb-1.5">
127
- Use auto-provider
128
- </p>
129
- <p className="text-xs text-neutral-400/70">
130
- We'll automatically select the best provider for you based
131
- on your prompt.
132
- </p>
133
- </div>
134
  <div
135
  className={classNames(
136
- "bg-neutral-700 rounded-full min-w-10 w-10 h-6 flex items-center justify-between p-1 cursor-pointer transition-all duration-200",
137
  {
138
- "!bg-sky-500": provider === "auto",
139
  }
140
  )}
141
- onClick={() => {
142
- const foundModel = MODELS.find(
143
- (m: { value: string }) => m.value === model
144
- );
145
- if (provider === "auto") {
146
- onChange(foundModel.autoProvider);
147
- } else {
148
- onChange("auto");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  }
 
 
 
150
  }}
151
  >
152
- <div
153
- className={classNames(
154
- "w-4 h-4 rounded-full shadow-md transition-all duration-200 bg-neutral-200",
155
- {
156
- "translate-x-4": provider === "auto",
157
- }
158
- )}
159
  />
 
160
  </div>
161
- </div>
162
- <label className="block">
163
- <p className="text-neutral-300 text-sm mb-2">
164
- Inference Provider
165
- </p>
166
- <div className="grid grid-cols-2 gap-1.5">
167
- {modelAvailableProviders.map((id: string) => (
168
- <Button
169
- key={id}
170
- variant={id === provider ? "default" : "secondary"}
171
- size="sm"
172
- onClick={() => {
173
- onChange(id);
174
- }}
175
- >
176
- <img
177
- src={`/providers/${id}.svg`}
178
- alt={PROVIDERS[id].name}
179
- className="size-5 mr-2"
180
- />
181
- {PROVIDERS[id].name}
182
- {id === provider && (
183
- <RiCheckboxCircleFill className="ml-2 size-4 text-blue-500" />
184
- )}
185
- </Button>
186
- ))}
187
- </div>
188
- </label>
189
  </div>
190
- </main>
191
- </PopoverContent>
192
- </Popover>
193
  </div>
194
  );
195
  }
 
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
  import classNames from "classnames";
 
 
3
 
4
+ import { PiGearSixFill } from "react-icons/pi";
5
  // @ts-expect-error not needed
6
+ import { PROVIDERS } from "./../../../utils/providers";
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  function Settings({
9
  open,
10
  onClose,
11
  provider,
 
12
  error,
13
  onChange,
 
14
  }: {
15
  open: boolean;
16
  provider: string;
 
17
  error?: string;
18
  onClose: React.Dispatch<React.SetStateAction<boolean>>;
19
  onChange: (provider: string) => void;
 
20
  }) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  return (
22
  <div className="">
23
+ <button
24
+ className="relative overflow-hidden cursor-pointer flex-none flex items-center justify-center rounded-full text-base font-semibold size-8 text-center bg-gray-800 hover:bg-gray-700 text-gray-100 shadow-sm dark:shadow-highlight/20"
25
+ onClick={() => {
26
+ onClose((prev) => !prev);
27
+ }}
28
+ >
29
+ <PiGearSixFill />
30
+ </button>
31
+ <div
32
+ className={classNames(
33
+ "h-screen w-screen bg-black/20 fixed left-0 top-0 z-40",
34
+ {
35
+ "opacity-0 pointer-events-none": !open,
36
+ }
37
+ )}
38
+ onClick={() => onClose(false)}
39
+ ></div>
40
+ <div
41
+ className={classNames(
42
+ "absolute top-0 -translate-y-[calc(100%+16px)] right-0 z-40 w-96 bg-white border border-gray-200 rounded-lg shadow-lg transition-all duration-75 overflow-hidden",
43
+ {
44
+ "opacity-0 pointer-events-none": !open,
45
+ }
46
+ )}
47
+ >
48
+ <header className="flex items-center text-sm px-4 py-2 border-b border-gray-200 gap-2 bg-gray-100 font-semibold text-gray-700">
49
+ <span className="text-xs bg-blue-500/10 text-blue-500 rounded-full pl-1.5 pr-2.5 py-0.5 flex items-center justify-start gap-1.5">
50
+ Provider
51
+ </span>
52
+ Customize Settings
53
+ </header>
54
+ <main className="px-4 pt-3 pb-4 space-y-4">
55
+ {/* toggle using tailwind css */}
56
+ <div>
57
  <a
58
  href="https://huggingface.co/spaces/enzostvs/deepsite/discussions/74"
59
  target="_blank"
60
+ className="w-full flex items-center justify-between text-gray-600 bg-gray-50 border border-gray-100 px-2 py-2 rounded-lg mb-3 text-sm font-medium hover:brightness-95"
61
  >
62
  How to use it locally?
63
+ <button className="bg-black text-white rounded-md px-3 py-1.5 text-xs font-semibold cursor-pointer">
64
+ See the guide
65
+ </button>
66
  </a>
67
+ <div className="flex items-center justify-between">
68
+ <p className="text-gray-800 text-sm font-medium flex items-center justify-between">
69
+ Use auto-provider
70
  </p>
71
+ <div
72
+ className={classNames(
73
+ "bg-gray-200 rounded-full w-10 h-6 flex items-center justify-between p-1 cursor-pointer transition-all duration-200",
74
+ {
75
+ "!bg-blue-500": provider === "auto",
76
+ }
77
+ )}
78
+ onClick={() => {
79
+ onChange(provider === "auto" ? "fireworks-ai" : "auto");
80
+ }}
81
+ >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  <div
83
  className={classNames(
84
+ "w-4 h-4 rounded-full shadow-md transition-all duration-200 bg-white",
85
  {
86
+ "translate-x-4": provider === "auto",
87
  }
88
  )}
89
+ />
90
+ </div>
91
+ </div>
92
+ <p className="text-xs text-gray-500 mt-2">
93
+ We'll automatically select the best provider for you based on your
94
+ prompt.
95
+ </p>
96
+ </div>
97
+ {error !== "" && (
98
+ <p className="text-red-500 text-sm font-medium mb-2 flex items-center justify-between bg-red-500/10 p-2 rounded-md">
99
+ {error}
100
+ </p>
101
+ )}
102
+ <label className="block">
103
+ <p className="text-gray-800 text-sm font-medium mb-2 flex items-center justify-between">
104
+ Inference Provider
105
+ </p>
106
+ <div className="grid grid-cols-2 gap-1.5">
107
+ {Object.keys(PROVIDERS).map((id: string) => (
108
+ <div
109
+ key={id}
110
+ className={classNames(
111
+ "text-gray-600 text-sm font-medium cursor-pointer border p-2 rounded-md flex items-center justify-start gap-2",
112
+ {
113
+ "bg-blue-500/10 border-blue-500/15 text-blue-500":
114
+ id === provider,
115
+ "hover:bg-gray-100 border-gray-100": id !== provider,
116
  }
117
+ )}
118
+ onClick={() => {
119
+ onChange(id);
120
  }}
121
  >
122
+ <img
123
+ src={`/providers/${id}.svg`}
124
+ alt={PROVIDERS[id].name}
125
+ className="size-5"
 
 
 
126
  />
127
+ {PROVIDERS[id].name}
128
  </div>
129
+ ))}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  </div>
131
+ </label>
132
+ </main>
133
+ </div>
134
  </div>
135
  );
136
  }
src/components/tabs/tabs.tsx ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from "react";
2
+ import classNames from "classnames";
3
+ import { IoTimeOutline } from "react-icons/io5";
4
+
5
+ import Deepseek from "./../../assets/deepseek-color.svg";
6
+
7
+ function Tabs({
8
+ htmlHistory,
9
+ setHtml,
10
+ children,
11
+ }: {
12
+ htmlHistory?: { html: string; createdAt: Date; prompt: string }[];
13
+ setHtml: (html: string) => void;
14
+ children?: React.ReactNode;
15
+ }) {
16
+ const [visible, setVisible] = useState(false);
17
+
18
+ return (
19
+ <div className="border-b border-gray-800 pl-4 lg:pl-7 pr-3 flex items-center justify-between">
20
+ <div className="flex items-center justify-start gap-4 flex-1">
21
+ <div
22
+ className="
23
+ space-x-6"
24
+ >
25
+ <button className="rounded-md text-sm cursor-pointer transition-all duration-100 font-medium relative py-2.5 text-white">
26
+ index.html
27
+ <span className="absolute bottom-0 left-0 h-0.5 w-full transition-all duration-100 bg-white" />
28
+ </button>
29
+ </div>
30
+ {htmlHistory && htmlHistory?.length > 1 && (
31
+ <div
32
+ className="relative"
33
+ onMouseEnter={() => setVisible(true)}
34
+ onMouseLeave={() => setVisible(false)}
35
+ >
36
+ <button
37
+ className={classNames(
38
+ "text-gray-400 hover:text-gray-300 cursor-pointer text-sm nderline flex items-center justify-start gap-1",
39
+ {
40
+ "!text-gray-300": visible,
41
+ }
42
+ )}
43
+ >
44
+ <IoTimeOutline />
45
+ {htmlHistory?.length} versions
46
+ </button>
47
+ <div
48
+ className={classNames(
49
+ "absolute bottom-0 left-0 min-w-sm w-full z-10 translate-y-full pt-2 transition-all duration-200",
50
+ {
51
+ "opacity-0 pointer-events-none": !visible,
52
+ }
53
+ )}
54
+ >
55
+ <div className="bg-gray-950 border border-gray-800 rounded-xl shadow-2xs p-4">
56
+ <p className="text-xs font-bold text-white">Version History</p>
57
+ <p className="text-gray-400 text-xs mt-1">
58
+ This is a list of the full history of what AI has done to
59
+ this.
60
+ </p>
61
+ <ul className="mt-2 max-h-[250px] overflow-y-auto">
62
+ {htmlHistory?.map((item, index) => (
63
+ <li
64
+ key={index}
65
+ className="text-gray-300 text-xs py-2 border-b border-gray-800 last:border-0 flex items-center justify-between gap-2"
66
+ >
67
+ <div className="">
68
+ <span className="line-clamp-1">{item.prompt}</span>
69
+ <span className="text-gray-500 text-[10px]">
70
+ {new Date(item.createdAt).toLocaleDateString(
71
+ "en-US",
72
+ {
73
+ month: "2-digit",
74
+ day: "2-digit",
75
+ year: "2-digit",
76
+ }
77
+ ) +
78
+ " " +
79
+ new Date(item.createdAt).toLocaleTimeString(
80
+ "en-US",
81
+ {
82
+ hour: "2-digit",
83
+ minute: "2-digit",
84
+ second: "2-digit",
85
+ hour12: false,
86
+ }
87
+ )}
88
+ </span>
89
+ </div>
90
+ <button
91
+ className="bg-pink-500 text-white text-xs font-medium rounded-md px-2 py-1 transition-all duration-100 hover:bg-pink-600 cursor-pointer"
92
+ onClick={() => {
93
+ setHtml(item.html);
94
+ }}
95
+ >
96
+ Select
97
+ </button>
98
+ </li>
99
+ ))}
100
+ </ul>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ )}
105
+ </div>
106
+ <div className="flex items-center justify-end gap-3">
107
+ <a
108
+ href="https://huggingface.co/deepseek-ai/DeepSeek-V3-0324"
109
+ target="_blank"
110
+ className="text-[12px] text-gray-300 hover:brightness-120 flex items-center gap-1 font-code"
111
+ >
112
+ Powered by <img src={Deepseek} className="size-5" /> Deepseek
113
+ </a>
114
+ {children}
115
+ </div>
116
+ </div>
117
+ );
118
+ }
119
+
120
+ export default Tabs;
src/components/theme/mode-toggle.tsx DELETED
@@ -1,37 +0,0 @@
1
- import { Moon, Sun } from "lucide-react";
2
-
3
- import { Button } from "./../ui/button";
4
- import {
5
- DropdownMenu,
6
- DropdownMenuContent,
7
- DropdownMenuItem,
8
- DropdownMenuTrigger,
9
- } from "./../ui/dropdown-menu";
10
- import { useTheme } from "./../theme/theme-provider";
11
-
12
- export function ModeToggle() {
13
- const { setTheme } = useTheme();
14
-
15
- return (
16
- <DropdownMenu>
17
- <DropdownMenuTrigger asChild>
18
- <Button variant="outline" size="icon">
19
- <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
20
- <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
21
- <span className="sr-only">Toggle theme</span>
22
- </Button>
23
- </DropdownMenuTrigger>
24
- <DropdownMenuContent align="end">
25
- <DropdownMenuItem onClick={() => setTheme("light")}>
26
- Light
27
- </DropdownMenuItem>
28
- <DropdownMenuItem onClick={() => setTheme("dark")}>
29
- Dark
30
- </DropdownMenuItem>
31
- <DropdownMenuItem onClick={() => setTheme("system")}>
32
- System
33
- </DropdownMenuItem>
34
- </DropdownMenuContent>
35
- </DropdownMenu>
36
- );
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/theme/theme-provider.tsx DELETED
@@ -1,75 +0,0 @@
1
- import { createContext, useContext, useEffect, useState } from "react";
2
-
3
- type Theme = "dark" | "light" | "system";
4
-
5
- type ThemeProviderProps = {
6
- children: React.ReactNode;
7
- defaultTheme?: Theme;
8
- storageKey?: string;
9
- className?: string;
10
- };
11
-
12
- type ThemeProviderState = {
13
- theme: Theme;
14
- setTheme: (theme: Theme) => void;
15
- };
16
-
17
- const initialState: ThemeProviderState = {
18
- theme: "system",
19
- setTheme: () => null,
20
- };
21
-
22
- const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
23
-
24
- export function ThemeProvider({
25
- children,
26
- defaultTheme = "system",
27
- storageKey = "vite-ui-theme",
28
- className,
29
- ...props
30
- }: ThemeProviderProps) {
31
- const [theme, setTheme] = useState<Theme>(
32
- () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
33
- );
34
-
35
- useEffect(() => {
36
- const root = window.document.documentElement;
37
-
38
- root.classList.remove("light", "dark");
39
-
40
- if (theme === "system") {
41
- const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
42
- .matches
43
- ? "dark"
44
- : "light";
45
-
46
- root.classList.add(systemTheme);
47
- return;
48
- }
49
-
50
- root.classList.add(theme);
51
- }, [theme]);
52
-
53
- const value = {
54
- theme,
55
- setTheme: (theme: Theme) => {
56
- localStorage.setItem(storageKey, theme);
57
- setTheme(theme);
58
- },
59
- };
60
-
61
- return (
62
- <ThemeProviderContext.Provider {...props} value={value}>
63
- <section className={className}>{children}</section>
64
- </ThemeProviderContext.Provider>
65
- );
66
- }
67
-
68
- export const useTheme = () => {
69
- const context = useContext(ThemeProviderContext);
70
-
71
- if (context === undefined)
72
- throw new Error("useTheme must be used within a ThemeProvider");
73
-
74
- return context;
75
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/avatar.tsx DELETED
@@ -1,51 +0,0 @@
1
- import * as React from "react";
2
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
3
-
4
- import { cn } from "./../../lib/utils";
5
-
6
- function Avatar({
7
- className,
8
- ...props
9
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
10
- return (
11
- <AvatarPrimitive.Root
12
- data-slot="avatar"
13
- className={cn(
14
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
15
- className
16
- )}
17
- {...props}
18
- />
19
- );
20
- }
21
-
22
- function AvatarImage({
23
- className,
24
- ...props
25
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
26
- return (
27
- <AvatarPrimitive.Image
28
- data-slot="avatar-image"
29
- className={cn("aspect-square size-full", className)}
30
- {...props}
31
- />
32
- );
33
- }
34
-
35
- function AvatarFallback({
36
- className,
37
- ...props
38
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
39
- return (
40
- <AvatarPrimitive.Fallback
41
- data-slot="avatar-fallback"
42
- className={cn(
43
- "bg-neutral-100 flex size-full items-center justify-center rounded-full dark:bg-neutral-800",
44
- className
45
- )}
46
- {...props}
47
- />
48
- );
49
- }
50
-
51
- export { Avatar, AvatarImage, AvatarFallback };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/button.tsx DELETED
@@ -1,64 +0,0 @@
1
- import * as React from "react";
2
- import { Slot } from "@radix-ui/react-slot";
3
- import { cva, type VariantProps } from "class-variance-authority";
4
-
5
- import { cn } from "./../../lib/utils";
6
-
7
- const buttonVariants = cva(
8
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 cursor-pointer [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-neutral-950 focus-visible:ring-neutral-950/50 focus-visible:ring-[3px] aria-invalid:ring-red-500/20 dark:aria-invalid:ring-red-500/40 aria-invalid:border-red-500 dark:focus-visible:border-neutral-300 dark:focus-visible:ring-neutral-300/50 dark:aria-invalid:ring-red-900/20 dark:dark:aria-invalid:ring-red-900/40 dark:aria-invalid:border-red-900",
9
- {
10
- variants: {
11
- variant: {
12
- default:
13
- "bg-neutral-900 text-neutral-50 shadow-xs hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90",
14
- destructive: "bg-red-600 text-white shadow-xs hover:bg-red-500",
15
- outline:
16
- "border bg-white shadow-xs hover:bg-neutral-100 hover:text-neutral-900 dark:bg-neutral-200/30 dark:border-neutral-200 dark:hover:bg-neutral-200/50 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50 dark:dark:bg-neutral-800/30 dark:dark:border-neutral-800 dark:dark:hover:bg-neutral-800/50",
17
- secondary:
18
- "bg-neutral-100 text-neutral-900 shadow-xs hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80",
19
- ghost:
20
- "hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-100/50 dark:hover:bg-neutral-800 dark:hover:text-neutral-50 dark:dark:hover:bg-neutral-800/50",
21
- link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50",
22
- pink: "bg-sky-500 text-white hover:brightness-110",
23
- gray: "bg-neutral-900 text-neutral-300 hover:brightness-110",
24
- ghostWhite: "bg-neutral-200/60 text-neutral-900 hover:bg-neutral-200",
25
- },
26
- size: {
27
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
28
- sm: "h-8 rounded-full text-[13px] gap-1.5 px-3",
29
- lg: "h-10 rounded-full px-6 has-[>svg]:px-4",
30
- icon: "size-9",
31
- iconXs: "size-7",
32
- iconXss: "size-6",
33
- xs: "h-6 text-xs rounded-full pl-2 pr-2 gap-1",
34
- },
35
- },
36
- defaultVariants: {
37
- variant: "default",
38
- size: "default",
39
- },
40
- }
41
- );
42
-
43
- function Button({
44
- className,
45
- variant,
46
- size,
47
- asChild = false,
48
- ...props
49
- }: React.ComponentProps<"button"> &
50
- VariantProps<typeof buttonVariants> & {
51
- asChild?: boolean;
52
- }) {
53
- const Comp = asChild ? Slot : "button";
54
-
55
- return (
56
- <Comp
57
- data-slot="button"
58
- className={cn(buttonVariants({ variant, size, className }))}
59
- {...props}
60
- />
61
- );
62
- }
63
-
64
- export { Button, buttonVariants };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/dialog.tsx DELETED
@@ -1,144 +0,0 @@
1
- import * as React from "react";
2
- import * as DialogPrimitive from "@radix-ui/react-dialog";
3
- import { XIcon } from "lucide-react";
4
-
5
- import { cn } from "./../../lib/utils";
6
-
7
- function Dialog({
8
- ...props
9
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
10
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
11
- }
12
-
13
- function DialogTrigger({
14
- ...props
15
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
16
- return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
17
- }
18
-
19
- function DialogPortal({
20
- ...props
21
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
22
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
23
- }
24
-
25
- function DialogClose({
26
- ...props
27
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
28
- return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
29
- }
30
-
31
- function DialogOverlay({
32
- className,
33
- ...props
34
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
35
- return (
36
- <DialogPrimitive.Overlay
37
- data-slot="dialog-overlay"
38
- className={cn(
39
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
40
- className
41
- )}
42
- {...props}
43
- />
44
- );
45
- }
46
-
47
- function DialogContent({
48
- className,
49
- children,
50
- showCloseButton = true,
51
- ...props
52
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
53
- showCloseButton?: boolean;
54
- }) {
55
- return (
56
- <DialogPortal data-slot="dialog-portal">
57
- <DialogOverlay />
58
- <DialogPrimitive.Content
59
- data-slot="dialog-content"
60
- className={cn(
61
- "bg-white data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-neutral-200 p-6 shadow-lg duration-200 sm:max-w-lg dark:bg-neutral-950 dark:border-neutral-800",
62
- className
63
- )}
64
- {...props}
65
- >
66
- {children}
67
- {showCloseButton && (
68
- <DialogPrimitive.Close
69
- data-slot="dialog-close"
70
- className="ring-offset-white focus:ring-neutral-950 data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-500 absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:ring-offset-neutral-950 dark:focus:ring-neutral-300 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-400"
71
- >
72
- <XIcon />
73
- <span className="sr-only">Close</span>
74
- </DialogPrimitive.Close>
75
- )}
76
- </DialogPrimitive.Content>
77
- </DialogPortal>
78
- );
79
- }
80
-
81
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
82
- return (
83
- <div
84
- data-slot="dialog-header"
85
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
86
- {...props}
87
- />
88
- );
89
- }
90
-
91
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
92
- return (
93
- <div
94
- data-slot="dialog-footer"
95
- className={cn(
96
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
97
- className
98
- )}
99
- {...props}
100
- />
101
- );
102
- }
103
-
104
- function DialogTitle({
105
- className,
106
- ...props
107
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
108
- return (
109
- <DialogPrimitive.Title
110
- data-slot="dialog-title"
111
- className={cn("text-lg leading-none font-semibold", className)}
112
- {...props}
113
- />
114
- );
115
- }
116
-
117
- function DialogDescription({
118
- className,
119
- ...props
120
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
121
- return (
122
- <DialogPrimitive.Description
123
- data-slot="dialog-description"
124
- className={cn(
125
- "text-neutral-500 text-sm dark:text-neutral-400",
126
- className
127
- )}
128
- {...props}
129
- />
130
- );
131
- }
132
-
133
- export {
134
- Dialog,
135
- DialogClose,
136
- DialogContent,
137
- DialogDescription,
138
- DialogFooter,
139
- DialogHeader,
140
- DialogOverlay,
141
- DialogPortal,
142
- DialogTitle,
143
- DialogTrigger,
144
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/dropdown-menu.tsx DELETED
@@ -1,258 +0,0 @@
1
- import * as React from "react";
2
- import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
3
- import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
4
-
5
- import { cn } from "./../../lib/utils";
6
-
7
- function DropdownMenu({
8
- ...props
9
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
10
- return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
11
- }
12
-
13
- function DropdownMenuPortal({
14
- ...props
15
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
16
- return (
17
- <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
18
- );
19
- }
20
-
21
- function DropdownMenuTrigger({
22
- ...props
23
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
24
- return (
25
- <DropdownMenuPrimitive.Trigger
26
- data-slot="dropdown-menu-trigger"
27
- {...props}
28
- />
29
- );
30
- }
31
-
32
- function DropdownMenuContent({
33
- className,
34
- sideOffset = 4,
35
- ...props
36
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
37
- return (
38
- <DropdownMenuPrimitive.Portal>
39
- <DropdownMenuPrimitive.Content
40
- data-slot="dropdown-menu-content"
41
- sideOffset={sideOffset}
42
- className={cn(
43
- "bg-white text-neutral-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-neutral-200 p-1 shadow-md dark:bg-neutral-950 dark:text-neutral-50 dark:border-neutral-800",
44
- className
45
- )}
46
- {...props}
47
- />
48
- </DropdownMenuPrimitive.Portal>
49
- );
50
- }
51
-
52
- function DropdownMenuGroup({
53
- ...props
54
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
55
- return (
56
- <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
57
- );
58
- }
59
-
60
- function DropdownMenuItem({
61
- className,
62
- inset,
63
- variant = "default",
64
- ...props
65
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
66
- inset?: boolean;
67
- variant?: "default" | "destructive";
68
- }) {
69
- return (
70
- <DropdownMenuPrimitive.Item
71
- data-slot="dropdown-menu-item"
72
- data-inset={inset}
73
- data-variant={variant}
74
- className={cn(
75
- "focus:bg-neutral-100 cursor-pointer focus:text-neutral-900 data-[variant=destructive]:text-red-500 data-[variant=destructive]:focus:bg-red-500/10 dark:data-[variant=destructive]:focus:bg-red-500/20 data-[variant=destructive]:focus:text-red-500 data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-neutral-500 relative flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:focus:bg-neutral-800 dark:focus:text-neutral-50 dark:data-[variant=destructive]:text-red-900 dark:data-[variant=destructive]:focus:bg-red-900/10 dark:dark:data-[variant=destructive]:focus:bg-red-900/20 dark:data-[variant=destructive]:focus:text-red-900 dark:[&_svg:not([class*='text-'])]:text-neutral-400",
76
- className
77
- )}
78
- {...props}
79
- />
80
- );
81
- }
82
-
83
- function DropdownMenuCheckboxItem({
84
- className,
85
- children,
86
- checked,
87
- ...props
88
- }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
89
- return (
90
- <DropdownMenuPrimitive.CheckboxItem
91
- data-slot="dropdown-menu-checkbox-item"
92
- className={cn(
93
- "focus:bg-neutral-100 focus:text-neutral-900 relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
94
- className
95
- )}
96
- checked={checked}
97
- {...props}
98
- >
99
- <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
100
- <DropdownMenuPrimitive.ItemIndicator>
101
- <CheckIcon className="size-4" />
102
- </DropdownMenuPrimitive.ItemIndicator>
103
- </span>
104
- {children}
105
- </DropdownMenuPrimitive.CheckboxItem>
106
- );
107
- }
108
-
109
- function DropdownMenuRadioGroup({
110
- ...props
111
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
112
- return (
113
- <DropdownMenuPrimitive.RadioGroup
114
- data-slot="dropdown-menu-radio-group"
115
- {...props}
116
- />
117
- );
118
- }
119
-
120
- function DropdownMenuRadioItem({
121
- className,
122
- children,
123
- ...props
124
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
125
- return (
126
- <DropdownMenuPrimitive.RadioItem
127
- data-slot="dropdown-menu-radio-item"
128
- className={cn(
129
- "focus:bg-neutral-100 focus:text-neutral-900 relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:focus:bg-neutral-800 dark:focus:text-neutral-50",
130
- className
131
- )}
132
- {...props}
133
- >
134
- <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
135
- <DropdownMenuPrimitive.ItemIndicator>
136
- <CircleIcon className="size-2 fill-current" />
137
- </DropdownMenuPrimitive.ItemIndicator>
138
- </span>
139
- {children}
140
- </DropdownMenuPrimitive.RadioItem>
141
- );
142
- }
143
-
144
- function DropdownMenuLabel({
145
- className,
146
- inset,
147
- ...props
148
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
149
- inset?: boolean;
150
- }) {
151
- return (
152
- <DropdownMenuPrimitive.Label
153
- data-slot="dropdown-menu-label"
154
- data-inset={inset}
155
- className={cn(
156
- "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
157
- className
158
- )}
159
- {...props}
160
- />
161
- );
162
- }
163
-
164
- function DropdownMenuSeparator({
165
- className,
166
- ...props
167
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
168
- return (
169
- <DropdownMenuPrimitive.Separator
170
- data-slot="dropdown-menu-separator"
171
- className={cn(
172
- "bg-neutral-200 -mx-1 my-1 h-px dark:bg-neutral-800",
173
- className
174
- )}
175
- {...props}
176
- />
177
- );
178
- }
179
-
180
- function DropdownMenuShortcut({
181
- className,
182
- ...props
183
- }: React.ComponentProps<"span">) {
184
- return (
185
- <span
186
- data-slot="dropdown-menu-shortcut"
187
- className={cn(
188
- "text-neutral-500 ml-auto text-xs tracking-widest dark:text-neutral-400",
189
- className
190
- )}
191
- {...props}
192
- />
193
- );
194
- }
195
-
196
- function DropdownMenuSub({
197
- ...props
198
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
199
- return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
200
- }
201
-
202
- function DropdownMenuSubTrigger({
203
- className,
204
- inset,
205
- children,
206
- ...props
207
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
208
- inset?: boolean;
209
- }) {
210
- return (
211
- <DropdownMenuPrimitive.SubTrigger
212
- data-slot="dropdown-menu-sub-trigger"
213
- data-inset={inset}
214
- className={cn(
215
- "focus:bg-neutral-100 focus:text-neutral-900 data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-900 flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 dark:focus:bg-neutral-800 dark:focus:text-neutral-50 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-50",
216
- className
217
- )}
218
- {...props}
219
- >
220
- {children}
221
- <ChevronRightIcon className="ml-auto size-4" />
222
- </DropdownMenuPrimitive.SubTrigger>
223
- );
224
- }
225
-
226
- function DropdownMenuSubContent({
227
- className,
228
- ...props
229
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
230
- return (
231
- <DropdownMenuPrimitive.SubContent
232
- data-slot="dropdown-menu-sub-content"
233
- className={cn(
234
- "bg-white text-neutral-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border border-neutral-200 p-1 shadow-lg dark:bg-neutral-950 dark:text-neutral-50 dark:border-neutral-800",
235
- className
236
- )}
237
- {...props}
238
- />
239
- );
240
- }
241
-
242
- export {
243
- DropdownMenu,
244
- DropdownMenuPortal,
245
- DropdownMenuTrigger,
246
- DropdownMenuContent,
247
- DropdownMenuGroup,
248
- DropdownMenuLabel,
249
- DropdownMenuItem,
250
- DropdownMenuCheckboxItem,
251
- DropdownMenuRadioGroup,
252
- DropdownMenuRadioItem,
253
- DropdownMenuSeparator,
254
- DropdownMenuShortcut,
255
- DropdownMenuSub,
256
- DropdownMenuSubTrigger,
257
- DropdownMenuSubContent,
258
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/input.tsx DELETED
@@ -1,21 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "./../../lib/utils";
4
-
5
- function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
- return (
7
- <input
8
- type={type}
9
- data-slot="input"
10
- className={cn(
11
- "file:text-neutral-950 placeholder:text-neutral-500 selection:bg-neutral-900 selection:text-neutral-50 dark:bg-neutral-200/30 border-neutral-200 flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:file:text-neutral-50 dark:placeholder:text-neutral-400 dark:selection:bg-neutral-50 dark:selection:text-neutral-900 dark:dark:bg-neutral-800/30 dark:border-neutral-800",
12
- "focus-visible:border-neutral-950 focus-visible:ring-neutral-950/50 focus-visible:ring-[3px] dark:focus-visible:border-neutral-300 dark:focus-visible:ring-neutral-300/50",
13
- "aria-invalid:ring-red-500/20 dark:aria-invalid:ring-red-500/40 aria-invalid:border-red-500 dark:aria-invalid:ring-red-900/20 dark:dark:aria-invalid:ring-red-900/40 dark:aria-invalid:border-red-900",
14
- className
15
- )}
16
- {...props}
17
- />
18
- );
19
- }
20
-
21
- export { Input };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/popover.tsx DELETED
@@ -1,46 +0,0 @@
1
- import * as React from "react";
2
- import * as PopoverPrimitive from "@radix-ui/react-popover";
3
-
4
- import { cn } from "./../../lib/utils";
5
-
6
- function Popover({
7
- ...props
8
- }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
9
- return <PopoverPrimitive.Root data-slot="popover" {...props} />;
10
- }
11
-
12
- function PopoverTrigger({
13
- ...props
14
- }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
15
- return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
16
- }
17
-
18
- function PopoverContent({
19
- className,
20
- align = "center",
21
- sideOffset = 4,
22
- ...props
23
- }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
24
- return (
25
- <PopoverPrimitive.Portal>
26
- <PopoverPrimitive.Content
27
- data-slot="popover-content"
28
- align={align}
29
- sideOffset={sideOffset}
30
- className={cn(
31
- "bg-white text-neutral-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border border-neutral-200 p-4 shadow-md outline-hidden dark:bg-neutral-950 dark:text-neutral-50 dark:border-neutral-800",
32
- className
33
- )}
34
- {...props}
35
- />
36
- </PopoverPrimitive.Portal>
37
- );
38
- }
39
-
40
- function PopoverAnchor({
41
- ...props
42
- }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
43
- return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
44
- }
45
-
46
- export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/select.tsx DELETED
@@ -1,189 +0,0 @@
1
- import * as React from "react";
2
- import * as SelectPrimitive from "@radix-ui/react-select";
3
- import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
4
-
5
- import { cn } from "./../../lib/utils";
6
-
7
- function Select({
8
- ...props
9
- }: React.ComponentProps<typeof SelectPrimitive.Root>) {
10
- return <SelectPrimitive.Root data-slot="select" {...props} />;
11
- }
12
-
13
- function SelectGroup({
14
- ...props
15
- }: React.ComponentProps<typeof SelectPrimitive.Group>) {
16
- return <SelectPrimitive.Group data-slot="select-group" {...props} />;
17
- }
18
-
19
- function SelectValue({
20
- ...props
21
- }: React.ComponentProps<typeof SelectPrimitive.Value>) {
22
- return <SelectPrimitive.Value data-slot="select-value" {...props} />;
23
- }
24
-
25
- function SelectTrigger({
26
- className,
27
- size = "default",
28
- children,
29
- ...props
30
- }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
31
- size?: "sm" | "default";
32
- }) {
33
- return (
34
- <SelectPrimitive.Trigger
35
- data-slot="select-trigger"
36
- data-size={size}
37
- className={cn(
38
- "border-neutral-200 data-[placeholder]:text-neutral-500 [&_svg:not([class*='text-'])]:text-neutral-500 focus-visible:border-neutral-950 focus-visible:ring-neutral-950/50 aria-invalid:ring-red-500/20 dark:aria-invalid:ring-red-500/40 aria-invalid:border-red-500 dark:bg-neutral-200/30 dark:hover:bg-neutral-200/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:border-neutral-800 dark:data-[placeholder]:text-neutral-400 dark:[&_svg:not([class*='text-'])]:text-neutral-400 dark:focus-visible:border-neutral-300 dark:focus-visible:ring-neutral-300/50 dark:aria-invalid:ring-red-900/20 dark:dark:aria-invalid:ring-red-900/40 dark:aria-invalid:border-red-900 dark:dark:bg-neutral-800/30 dark:dark:hover:bg-neutral-800/50",
39
- className
40
- )}
41
- {...props}
42
- >
43
- {children}
44
- <SelectPrimitive.Icon asChild>
45
- <ChevronDownIcon className="size-4 opacity-50" />
46
- </SelectPrimitive.Icon>
47
- </SelectPrimitive.Trigger>
48
- );
49
- }
50
-
51
- function SelectContent({
52
- className,
53
- children,
54
- position = "popper",
55
- ...props
56
- }: React.ComponentProps<typeof SelectPrimitive.Content>) {
57
- return (
58
- <SelectPrimitive.Portal>
59
- <SelectPrimitive.Content
60
- data-slot="select-content"
61
- className={cn(
62
- "bg-white text-neutral-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-neutral-200 shadow-md dark:bg-neutral-950 dark:text-neutral-50 dark:border-neutral-800",
63
- position === "popper" &&
64
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
65
- className
66
- )}
67
- position={position}
68
- {...props}
69
- >
70
- <SelectScrollUpButton />
71
- <SelectPrimitive.Viewport
72
- className={cn(
73
- "p-1",
74
- position === "popper" &&
75
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
76
- )}
77
- >
78
- {children}
79
- </SelectPrimitive.Viewport>
80
- <SelectScrollDownButton />
81
- </SelectPrimitive.Content>
82
- </SelectPrimitive.Portal>
83
- );
84
- }
85
-
86
- function SelectLabel({
87
- className,
88
- ...props
89
- }: React.ComponentProps<typeof SelectPrimitive.Label>) {
90
- return (
91
- <SelectPrimitive.Label
92
- data-slot="select-label"
93
- className={cn(
94
- "text-neutral-500 px-2 py-1.5 text-xs dark:text-neutral-400",
95
- className
96
- )}
97
- {...props}
98
- />
99
- );
100
- }
101
-
102
- function SelectItem({
103
- className,
104
- children,
105
- ...props
106
- }: React.ComponentProps<typeof SelectPrimitive.Item>) {
107
- return (
108
- <SelectPrimitive.Item
109
- data-slot="select-item"
110
- className={cn(
111
- "focus:bg-neutral-100 focus:text-neutral-900 [&_svg:not([class*='text-'])]:text-neutral-500 relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 dark:focus:bg-neutral-800 dark:focus:text-neutral-50 dark:[&_svg:not([class*='text-'])]:text-neutral-400",
112
- className
113
- )}
114
- {...props}
115
- >
116
- <span className="absolute right-2 flex size-3.5 items-center justify-center">
117
- <SelectPrimitive.ItemIndicator>
118
- <CheckIcon className="size-4" />
119
- </SelectPrimitive.ItemIndicator>
120
- </span>
121
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
122
- </SelectPrimitive.Item>
123
- );
124
- }
125
-
126
- function SelectSeparator({
127
- className,
128
- ...props
129
- }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
130
- return (
131
- <SelectPrimitive.Separator
132
- data-slot="select-separator"
133
- className={cn(
134
- "bg-neutral-200 pointer-events-none -mx-1 my-1 h-px dark:bg-neutral-800",
135
- className
136
- )}
137
- {...props}
138
- />
139
- );
140
- }
141
-
142
- function SelectScrollUpButton({
143
- className,
144
- ...props
145
- }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
146
- return (
147
- <SelectPrimitive.ScrollUpButton
148
- data-slot="select-scroll-up-button"
149
- className={cn(
150
- "flex cursor-default items-center justify-center py-1",
151
- className
152
- )}
153
- {...props}
154
- >
155
- <ChevronUpIcon className="size-4" />
156
- </SelectPrimitive.ScrollUpButton>
157
- );
158
- }
159
-
160
- function SelectScrollDownButton({
161
- className,
162
- ...props
163
- }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
164
- return (
165
- <SelectPrimitive.ScrollDownButton
166
- data-slot="select-scroll-down-button"
167
- className={cn(
168
- "flex cursor-default items-center justify-center py-1",
169
- className
170
- )}
171
- {...props}
172
- >
173
- <ChevronDownIcon className="size-4" />
174
- </SelectPrimitive.ScrollDownButton>
175
- );
176
- }
177
-
178
- export {
179
- Select,
180
- SelectContent,
181
- SelectGroup,
182
- SelectItem,
183
- SelectLabel,
184
- SelectScrollDownButton,
185
- SelectScrollUpButton,
186
- SelectSeparator,
187
- SelectTrigger,
188
- SelectValue,
189
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/sonner.tsx DELETED
@@ -1,23 +0,0 @@
1
- import { useTheme } from "next-themes"
2
- import { Toaster as Sonner, ToasterProps } from "sonner"
3
-
4
- const Toaster = ({ ...props }: ToasterProps) => {
5
- const { theme = "system" } = useTheme()
6
-
7
- return (
8
- <Sonner
9
- theme={theme as ToasterProps["theme"]}
10
- className="toaster group"
11
- style={
12
- {
13
- "--normal-bg": "var(--popover)",
14
- "--normal-text": "var(--popover-foreground)",
15
- "--normal-border": "var(--border)",
16
- } as React.CSSProperties
17
- }
18
- {...props}
19
- />
20
- )
21
- }
22
-
23
- export { Toaster }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/tabs.tsx DELETED
@@ -1,64 +0,0 @@
1
- import * as React from "react";
2
- import * as TabsPrimitive from "@radix-ui/react-tabs";
3
-
4
- import { cn } from "./../../lib/utils";
5
-
6
- function Tabs({
7
- className,
8
- ...props
9
- }: React.ComponentProps<typeof TabsPrimitive.Root>) {
10
- return (
11
- <TabsPrimitive.Root
12
- data-slot="tabs"
13
- className={cn("flex flex-col gap-2", className)}
14
- {...props}
15
- />
16
- );
17
- }
18
-
19
- function TabsList({
20
- className,
21
- ...props
22
- }: React.ComponentProps<typeof TabsPrimitive.List>) {
23
- return (
24
- <TabsPrimitive.List
25
- data-slot="tabs-list"
26
- className={cn(
27
- "bg-neutral-100 text-neutral-500 inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px] dark:bg-neutral-800 dark:text-neutral-400",
28
- className
29
- )}
30
- {...props}
31
- />
32
- );
33
- }
34
-
35
- function TabsTrigger({
36
- className,
37
- ...props
38
- }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
39
- return (
40
- <TabsPrimitive.Trigger
41
- data-slot="tabs-trigger"
42
- className={cn(
43
- "data-[state=active]:bg-white dark:data-[state=active]:text-neutral-950 focus-visible:border-neutral-950 focus-visible:ring-neutral-950/50 focus-visible:outline-ring dark:data-[state=active]:border-neutral-200 dark:data-[state=active]:bg-neutral-200/30 text-neutral-950 dark:text-neutral-500 inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-neutral-200 border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 dark:data-[state=active]:bg-neutral-950 dark:dark:data-[state=active]:text-neutral-50 dark:focus-visible:border-neutral-300 dark:focus-visible:ring-neutral-300/50 dark:dark:data-[state=active]:border-neutral-800 dark:dark:data-[state=active]:bg-neutral-800/30 dark:text-neutral-50 dark:dark:text-neutral-400 dark:border-neutral-800",
44
- className
45
- )}
46
- {...props}
47
- />
48
- );
49
- }
50
-
51
- function TabsContent({
52
- className,
53
- ...props
54
- }: React.ComponentProps<typeof TabsPrimitive.Content>) {
55
- return (
56
- <TabsPrimitive.Content
57
- data-slot="tabs-content"
58
- className={cn("flex-1 outline-none", className)}
59
- {...props}
60
- />
61
- );
62
- }
63
-
64
- export { Tabs, TabsList, TabsTrigger, TabsContent };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/toggle-group.tsx DELETED
@@ -1,71 +0,0 @@
1
- import * as React from "react";
2
- import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
3
- import { type VariantProps } from "class-variance-authority";
4
-
5
- import { cn } from "./../../lib/utils";
6
- import { toggleVariants } from "./../ui/toggle";
7
-
8
- const ToggleGroupContext = React.createContext<
9
- VariantProps<typeof toggleVariants>
10
- >({
11
- size: "default",
12
- variant: "default",
13
- });
14
-
15
- function ToggleGroup({
16
- className,
17
- variant,
18
- size,
19
- children,
20
- ...props
21
- }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
22
- VariantProps<typeof toggleVariants>) {
23
- return (
24
- <ToggleGroupPrimitive.Root
25
- data-slot="toggle-group"
26
- data-variant={variant}
27
- data-size={size}
28
- className={cn(
29
- "group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
30
- className
31
- )}
32
- {...props}
33
- >
34
- <ToggleGroupContext.Provider value={{ variant, size }}>
35
- {children}
36
- </ToggleGroupContext.Provider>
37
- </ToggleGroupPrimitive.Root>
38
- );
39
- }
40
-
41
- function ToggleGroupItem({
42
- className,
43
- children,
44
- variant,
45
- size,
46
- ...props
47
- }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
48
- VariantProps<typeof toggleVariants>) {
49
- const context = React.useContext(ToggleGroupContext);
50
-
51
- return (
52
- <ToggleGroupPrimitive.Item
53
- data-slot="toggle-group-item"
54
- data-variant={context.variant || variant}
55
- data-size={context.size || size}
56
- className={cn(
57
- toggleVariants({
58
- variant: context.variant || variant,
59
- size: context.size || size,
60
- }),
61
- "min-w-0 !pl-3 flex-1 cursor-pointer shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
62
- className
63
- )}
64
- {...props}
65
- >
66
- {children}
67
- </ToggleGroupPrimitive.Item>
68
- );
69
- }
70
-
71
- export { ToggleGroup, ToggleGroupItem };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/toggle.tsx DELETED
@@ -1,47 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import * as TogglePrimitive from "@radix-ui/react-toggle";
5
- import { cva, type VariantProps } from "class-variance-authority";
6
-
7
- import { cn } from "./../../lib/utils";
8
-
9
- const toggleVariants = cva(
10
- "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-neutral-100 hover:text-neutral-500 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-neutral-100 data-[state=on]:text-neutral-900 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-neutral-950 focus-visible:ring-neutral-950/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-red-500/20 dark:aria-invalid:ring-red-500/40 aria-invalid:border-red-500 whitespace-nowrap dark:hover:bg-neutral-800 dark:hover:text-neutral-400 dark:data-[state=on]:bg-neutral-800 dark:data-[state=on]:text-neutral-50 dark:focus-visible:border-neutral-300 dark:focus-visible:ring-neutral-300/50 dark:aria-invalid:ring-red-900/20 dark:dark:aria-invalid:ring-red-900/40 dark:aria-invalid:border-red-900",
11
- {
12
- variants: {
13
- variant: {
14
- default: "bg-transparent",
15
- outline:
16
- "border border-neutral-200 bg-transparent shadow-xs hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:hover:bg-neutral-800 dark:hover:text-neutral-50",
17
- },
18
- size: {
19
- default: "h-9 px-2 min-w-9",
20
- sm: "h-8 px-1.5 min-w-8",
21
- lg: "h-10 px-2.5 min-w-10",
22
- },
23
- },
24
- defaultVariants: {
25
- variant: "default",
26
- size: "default",
27
- },
28
- }
29
- );
30
-
31
- function Toggle({
32
- className,
33
- variant,
34
- size,
35
- ...props
36
- }: React.ComponentProps<typeof TogglePrimitive.Root> &
37
- VariantProps<typeof toggleVariants>) {
38
- return (
39
- <TogglePrimitive.Root
40
- data-slot="toggle"
41
- className={cn(toggleVariants({ variant, size, className }))}
42
- {...props}
43
- />
44
- );
45
- }
46
-
47
- export { Toggle, toggleVariants };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/ui/tooltip.tsx DELETED
@@ -1,59 +0,0 @@
1
- import * as React from "react";
2
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
3
-
4
- import { cn } from "./../../lib/utils";
5
-
6
- function TooltipProvider({
7
- delayDuration = 0,
8
- ...props
9
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
10
- return (
11
- <TooltipPrimitive.Provider
12
- data-slot="tooltip-provider"
13
- delayDuration={delayDuration}
14
- {...props}
15
- />
16
- );
17
- }
18
-
19
- function Tooltip({
20
- ...props
21
- }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22
- return (
23
- <TooltipProvider>
24
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
- </TooltipProvider>
26
- );
27
- }
28
-
29
- function TooltipTrigger({
30
- ...props
31
- }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
32
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
33
- }
34
-
35
- function TooltipContent({
36
- className,
37
- sideOffset = 0,
38
- children,
39
- ...props
40
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
41
- return (
42
- <TooltipPrimitive.Portal>
43
- <TooltipPrimitive.Content
44
- data-slot="tooltip-content"
45
- sideOffset={sideOffset}
46
- className={cn(
47
- "bg-black text-neutral-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
48
- className
49
- )}
50
- {...props}
51
- >
52
- {children}
53
- <TooltipPrimitive.Arrow className="bg-black fill-black z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
54
- </TooltipPrimitive.Content>
55
- </TooltipPrimitive.Portal>
56
- );
57
- }
58
-
59
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/utils.ts DELETED
@@ -1,6 +0,0 @@
1
- import { clsx, type ClassValue } from "clsx"
2
- import { twMerge } from "tailwind-merge"
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs))
6
- }
 
 
 
 
 
 
 
src/main.tsx CHANGED
@@ -1,10 +1,12 @@
1
  import { StrictMode } from "react";
2
  import { createRoot } from "react-dom/client";
 
3
  import "./assets/index.css";
4
- import App from "./views/App.tsx";
5
 
6
  createRoot(document.getElementById("root")!).render(
7
  <StrictMode>
8
  <App />
 
9
  </StrictMode>
10
  );
 
1
  import { StrictMode } from "react";
2
  import { createRoot } from "react-dom/client";
3
+ import { ToastContainer } from "react-toastify";
4
  import "./assets/index.css";
5
+ import App from "./components/App.tsx";
6
 
7
  createRoot(document.getElementById("root")!).render(
8
  <StrictMode>
9
  <App />
10
+ <ToastContainer className="pt-11 max-md:p-4" />
11
  </StrictMode>
12
  );
tsconfig.app.json CHANGED
@@ -6,7 +6,6 @@
6
  "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
  "module": "ESNext",
8
  "skipLibCheck": true,
9
- "composite": true,
10
 
11
  /* Bundler mode */
12
  "moduleResolution": "bundler",
 
6
  "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
  "module": "ESNext",
8
  "skipLibCheck": true,
 
9
 
10
  /* Bundler mode */
11
  "moduleResolution": "bundler",
tsconfig.json CHANGED
@@ -1,17 +1,13 @@
1
  {
2
  "files": [],
3
  "references": [
4
- {
5
- "path": "./tsconfig.app.json"
6
- },
7
- {
8
- "path": "./tsconfig.node.json"
9
- }
10
  ],
11
  "compilerOptions": {
12
  "baseUrl": ".",
13
  "paths": {
14
- "@/*": ["./src/*"]
15
  }
16
  }
17
  }
 
1
  {
2
  "files": [],
3
  "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
 
 
 
 
6
  ],
7
  "compilerOptions": {
8
  "baseUrl": ".",
9
  "paths": {
10
+ "@/*": ["src/*"]
11
  }
12
  }
13
  }
utils/consts.ts CHANGED
@@ -4,17 +4,38 @@ export const defaultHTML = `<!DOCTYPE html>
4
  <title>My app</title>
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <meta charset="utf-8">
7
- <script src="https://cdn.tailwindcss.com"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  </head>
9
- <body class="flex justify-center items-center h-screen overflow-hidden bg-white font-sans text-center px-6">
10
- <div class="w-full">
11
- <span class="text-xs rounded-full mb-2 inline-block px-2 py-1 border border-amber-500/15 bg-amber-500/15 text-amber-500">🔥 New version dropped!</span>
12
- <h1 class="text-4xl lg:text-6xl font-bold font-sans">
13
- <span class="text-2xl lg:text-4xl text-gray-400 block font-medium">I'm ready to work,</span>
14
- Ask me anything.
15
- </h1>
16
- </div>
17
- <img src="https://enzostvs-deepsite.hf.space/arrow.svg" class="absolute bottom-8 left-0 w-[100px] transform rotate-[30deg]" />
18
  <script></script>
19
  </body>
20
  </html>
 
4
  <title>My app</title>
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <meta charset="utf-8">
7
+ <style>
8
+ body {
9
+ display: flex;
10
+ justify-content: center;
11
+ align-items: center;
12
+ overflow: hidden;
13
+ height: 100dvh;
14
+ font-family: "Arial", sans-serif;
15
+ text-align: center;
16
+ }
17
+ .arrow {
18
+ position: absolute;
19
+ bottom: 32px;
20
+ left: 0px;
21
+ width: 100px;
22
+ transform: rotate(30deg);
23
+ }
24
+ h1 {
25
+ font-size: 50px;
26
+ }
27
+ h1 span {
28
+ color: #acacac;
29
+ font-size: 32px;
30
+ }
31
+ </style>
32
  </head>
33
+ <body>
34
+ <h1>
35
+ <span>I'm ready to work,</span><br />
36
+ Ask me anything.
37
+ </h1>
38
+ <img src="https://enzostvs-deepsite.hf.space/arrow.svg" class="arrow" />
 
 
 
39
  <script></script>
40
  </body>
41
  </html>
utils/providers.js CHANGED
@@ -11,7 +11,7 @@ export const PROVIDERS = {
11
  },
12
  sambanova: {
13
  name: "SambaNova",
14
- max_tokens: 32_000,
15
  id: "sambanova",
16
  },
17
  novita: {
@@ -19,38 +19,9 @@ export const PROVIDERS = {
19
  max_tokens: 16_000,
20
  id: "novita",
21
  },
22
- hyperbolic: {
23
- name: "Hyperbolic",
24
- max_tokens: 131_000,
25
- id: "hyperbolic",
26
- },
27
- together: {
28
- name: "Together AI",
29
- max_tokens: 128_000,
30
- id: "together",
31
- },
32
  };
33
-
34
- export const MODELS = [
35
- {
36
- value: "deepseek-ai/DeepSeek-V3-0324",
37
- label: "DeepSeek V3 O324",
38
- providers: ["fireworks-ai", "nebius", "sambanova", "novita", "hyperbolic"],
39
- autoProvider: "fireworks-ai",
40
- },
41
- {
42
- value: "deepseek-ai/DeepSeek-R1-0528",
43
- label: "DeepSeek R1 0528",
44
- providers: [
45
- "fireworks-ai",
46
- "novita",
47
- "hyperbolic",
48
- "nebius",
49
- "together",
50
- "sambanova",
51
- ],
52
- autoProvider: "novita",
53
- isNew: true,
54
- isThinker: true,
55
- },
56
- ];
 
11
  },
12
  sambanova: {
13
  name: "SambaNova",
14
+ max_tokens: 8_000,
15
  id: "sambanova",
16
  },
17
  novita: {
 
19
  max_tokens: 16_000,
20
  id: "novita",
21
  },
22
+ // hyperbolic: {
23
+ // name: "Hyperbolic",
24
+ // max_tokens: 131_000,
25
+ // id: "hyperbolic",
26
+ // },
 
 
 
 
 
27
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
utils/types.ts CHANGED
@@ -4,9 +4,3 @@ export interface Auth {
4
  name: string;
5
  isLocalUse?: boolean;
6
  }
7
-
8
- export interface HtmlHistory {
9
- html: string;
10
- createdAt: Date;
11
- prompt: string;
12
- }
 
4
  name: string;
5
  isLocalUse?: boolean;
6
  }
 
 
 
 
 
 
vite.config.ts CHANGED
@@ -1,14 +1,11 @@
1
- import path from "path";
2
- import tailwindcss from "@tailwindcss/vite";
3
- import react from "@vitejs/plugin-react";
4
  import { defineConfig } from "vite";
 
 
5
 
6
  // https://vite.dev/config/
7
  export default defineConfig({
8
  plugins: [react(), tailwindcss()],
9
  resolve: {
10
- alias: {
11
- "@": path.resolve(__dirname, "./src"),
12
- },
13
  },
14
  });
 
 
 
 
1
  import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import tailwindcss from "@tailwindcss/vite";
4
 
5
  // https://vite.dev/config/
6
  export default defineConfig({
7
  plugins: [react(), tailwindcss()],
8
  resolve: {
9
+ alias: [{ find: "@", replacement: "/src" }],
 
 
10
  },
11
  });