Spaces:
Running
Running
DeepSite v2 🐳
Browse files- README.md +4 -1
- components.json +23 -0
- package-lock.json +1090 -8
- package.json +17 -0
- server.js +42 -13
- src/assets/index.css +129 -0
- src/components/ask-ai/ask-ai.tsx +83 -46
- src/components/deploy-button/deploy-button.tsx +81 -138
- src/components/footer/footer.tsx +177 -0
- src/components/header/header.tsx +45 -18
- src/components/history/history.tsx +138 -0
- src/components/load-button/load-button.tsx +43 -51
- src/components/login/login.tsx +4 -4
- src/components/magicui/grid-pattern.tsx +69 -0
- src/components/preview/preview.tsx +24 -90
- src/components/settings/settings.tsx +153 -95
- src/components/tabs/tabs.tsx +0 -120
- src/components/theme/mode-toggle.tsx +37 -0
- src/components/theme/theme-provider.tsx +75 -0
- src/components/ui/avatar.tsx +51 -0
- src/components/ui/button.tsx +63 -0
- src/components/ui/dropdown-menu.tsx +258 -0
- src/components/ui/input.tsx +21 -0
- src/components/ui/popover.tsx +46 -0
- src/components/ui/select.tsx +189 -0
- src/components/ui/sonner.tsx +23 -0
- src/components/ui/tabs.tsx +64 -0
- src/components/ui/toggle-group.tsx +71 -0
- src/components/ui/toggle.tsx +47 -0
- src/lib/utils.ts +6 -0
- src/main.tsx +1 -3
- src/{components → views}/App.tsx +148 -136
- tsconfig.app.json +1 -0
- tsconfig.json +7 -3
- utils/providers.js +22 -5
- utils/types.ts +6 -0
- vite.config.ts +6 -3
README.md
CHANGED
|
@@ -10,10 +10,13 @@ license: mit
|
|
| 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 |
-
|
|
|
|
|
|
| 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)
|
components.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
package-lock.json
CHANGED
|
@@ -11,13 +11,26 @@
|
|
| 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,11 +38,14 @@
|
|
| 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,6 +54,7 @@
|
|
| 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"
|
|
@@ -845,6 +862,44 @@
|
|
| 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",
|
|
@@ -1104,6 +1159,821 @@
|
|
| 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,11 +2572,12 @@
|
|
| 1702 |
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
|
| 1703 |
},
|
| 1704 |
"node_modules/@types/node": {
|
| 1705 |
-
"version": "22.
|
| 1706 |
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.
|
| 1707 |
-
"integrity": "sha512-
|
|
|
|
| 1708 |
"dependencies": {
|
| 1709 |
-
"undici-types": "~6.
|
| 1710 |
}
|
| 1711 |
},
|
| 1712 |
"node_modules/@types/qs": {
|
|
@@ -1733,7 +2604,7 @@
|
|
| 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 |
-
"
|
| 1737 |
"peerDependencies": {
|
| 1738 |
"@types/react": "^19.0.0"
|
| 1739 |
}
|
|
@@ -2101,6 +2972,18 @@
|
|
| 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,6 +3348,18 @@
|
|
| 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,6 +3369,7 @@
|
|
| 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,6 +3620,12 @@
|
|
| 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,6 +4294,15 @@
|
|
| 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,6 +5012,15 @@
|
|
| 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,6 +5778,16 @@
|
|
| 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,6 +6317,53 @@
|
|
| 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,6 +6372,28 @@
|
|
| 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,6 +6874,16 @@
|
|
| 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,6 +7040,16 @@
|
|
| 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,6 +7178,16 @@
|
|
| 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,9 +7248,10 @@
|
|
| 6219 |
}
|
| 6220 |
},
|
| 6221 |
"node_modules/undici-types": {
|
| 6222 |
-
"version": "6.
|
| 6223 |
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.
|
| 6224 |
-
"integrity": "sha512-
|
|
|
|
| 6225 |
},
|
| 6226 |
"node_modules/unified": {
|
| 6227 |
"version": "11.0.5",
|
|
@@ -6351,6 +7381,58 @@
|
|
| 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",
|
|
|
|
| 11 |
"@huggingface/hub": "^1.1.1",
|
| 12 |
"@huggingface/inference": "^3.6.1",
|
| 13 |
"@monaco-editor/react": "^4.7.0",
|
| 14 |
+
"@radix-ui/react-avatar": "^1.1.10",
|
| 15 |
+
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
| 16 |
+
"@radix-ui/react-popover": "^1.1.14",
|
| 17 |
+
"@radix-ui/react-select": "^2.2.5",
|
| 18 |
+
"@radix-ui/react-slot": "^1.2.3",
|
| 19 |
+
"@radix-ui/react-switch": "^1.2.5",
|
| 20 |
+
"@radix-ui/react-tabs": "^1.1.12",
|
| 21 |
+
"@radix-ui/react-toggle": "^1.1.9",
|
| 22 |
+
"@radix-ui/react-toggle-group": "^1.1.10",
|
| 23 |
"@tailwindcss/vite": "^4.0.15",
|
| 24 |
"@xenova/transformers": "^2.17.2",
|
| 25 |
"body-parser": "^1.20.3",
|
| 26 |
+
"class-variance-authority": "^0.7.1",
|
| 27 |
"classnames": "^2.5.1",
|
| 28 |
+
"clsx": "^2.1.1",
|
| 29 |
"cookie-parser": "^1.4.7",
|
| 30 |
"dotenv": "^16.4.7",
|
| 31 |
"express": "^4.21.2",
|
| 32 |
+
"lucide-react": "^0.511.0",
|
| 33 |
+
"next-themes": "^0.4.6",
|
| 34 |
"react": "^19.0.0",
|
| 35 |
"react-dom": "^19.0.0",
|
| 36 |
"react-icons": "^5.5.0",
|
|
|
|
| 38 |
"react-speech-recognition": "^4.0.0",
|
| 39 |
"react-toastify": "^11.0.5",
|
| 40 |
"react-use": "^17.6.0",
|
| 41 |
+
"sonner": "^2.0.3",
|
| 42 |
+
"tailwind-merge": "^3.3.0",
|
| 43 |
"tailwindcss": "^4.0.15"
|
| 44 |
},
|
| 45 |
"devDependencies": {
|
| 46 |
"@eslint/js": "^9.21.0",
|
| 47 |
"@types/express": "^5.0.1",
|
| 48 |
+
"@types/node": "^22.15.21",
|
| 49 |
"@types/react": "^19.0.10",
|
| 50 |
"@types/react-dom": "^19.0.4",
|
| 51 |
"@types/react-speech-recognition": "^3.9.6",
|
|
|
|
| 54 |
"eslint-plugin-react-hooks": "^5.1.0",
|
| 55 |
"eslint-plugin-react-refresh": "^0.4.19",
|
| 56 |
"globals": "^15.15.0",
|
| 57 |
+
"tw-animate-css": "^1.3.0",
|
| 58 |
"typescript": "~5.7.2",
|
| 59 |
"typescript-eslint": "^8.24.1",
|
| 60 |
"vite": "^6.2.0"
|
|
|
|
| 862 |
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
| 863 |
}
|
| 864 |
},
|
| 865 |
+
"node_modules/@floating-ui/core": {
|
| 866 |
+
"version": "1.7.0",
|
| 867 |
+
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz",
|
| 868 |
+
"integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==",
|
| 869 |
+
"license": "MIT",
|
| 870 |
+
"dependencies": {
|
| 871 |
+
"@floating-ui/utils": "^0.2.9"
|
| 872 |
+
}
|
| 873 |
+
},
|
| 874 |
+
"node_modules/@floating-ui/dom": {
|
| 875 |
+
"version": "1.7.0",
|
| 876 |
+
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz",
|
| 877 |
+
"integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==",
|
| 878 |
+
"license": "MIT",
|
| 879 |
+
"dependencies": {
|
| 880 |
+
"@floating-ui/core": "^1.7.0",
|
| 881 |
+
"@floating-ui/utils": "^0.2.9"
|
| 882 |
+
}
|
| 883 |
+
},
|
| 884 |
+
"node_modules/@floating-ui/react-dom": {
|
| 885 |
+
"version": "2.1.2",
|
| 886 |
+
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
|
| 887 |
+
"integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
|
| 888 |
+
"license": "MIT",
|
| 889 |
+
"dependencies": {
|
| 890 |
+
"@floating-ui/dom": "^1.0.0"
|
| 891 |
+
},
|
| 892 |
+
"peerDependencies": {
|
| 893 |
+
"react": ">=16.8.0",
|
| 894 |
+
"react-dom": ">=16.8.0"
|
| 895 |
+
}
|
| 896 |
+
},
|
| 897 |
+
"node_modules/@floating-ui/utils": {
|
| 898 |
+
"version": "0.2.9",
|
| 899 |
+
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
| 900 |
+
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
| 901 |
+
"license": "MIT"
|
| 902 |
+
},
|
| 903 |
"node_modules/@huggingface/hub": {
|
| 904 |
"version": "1.1.1",
|
| 905 |
"resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-1.1.1.tgz",
|
|
|
|
| 1159 |
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
| 1160 |
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
| 1161 |
},
|
| 1162 |
+
"node_modules/@radix-ui/number": {
|
| 1163 |
+
"version": "1.1.1",
|
| 1164 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
|
| 1165 |
+
"integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
|
| 1166 |
+
"license": "MIT"
|
| 1167 |
+
},
|
| 1168 |
+
"node_modules/@radix-ui/primitive": {
|
| 1169 |
+
"version": "1.1.2",
|
| 1170 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
|
| 1171 |
+
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
|
| 1172 |
+
"license": "MIT"
|
| 1173 |
+
},
|
| 1174 |
+
"node_modules/@radix-ui/react-arrow": {
|
| 1175 |
+
"version": "1.1.7",
|
| 1176 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
| 1177 |
+
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
|
| 1178 |
+
"license": "MIT",
|
| 1179 |
+
"dependencies": {
|
| 1180 |
+
"@radix-ui/react-primitive": "2.1.3"
|
| 1181 |
+
},
|
| 1182 |
+
"peerDependencies": {
|
| 1183 |
+
"@types/react": "*",
|
| 1184 |
+
"@types/react-dom": "*",
|
| 1185 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1186 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1187 |
+
},
|
| 1188 |
+
"peerDependenciesMeta": {
|
| 1189 |
+
"@types/react": {
|
| 1190 |
+
"optional": true
|
| 1191 |
+
},
|
| 1192 |
+
"@types/react-dom": {
|
| 1193 |
+
"optional": true
|
| 1194 |
+
}
|
| 1195 |
+
}
|
| 1196 |
+
},
|
| 1197 |
+
"node_modules/@radix-ui/react-avatar": {
|
| 1198 |
+
"version": "1.1.10",
|
| 1199 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz",
|
| 1200 |
+
"integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==",
|
| 1201 |
+
"license": "MIT",
|
| 1202 |
+
"dependencies": {
|
| 1203 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1204 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1205 |
+
"@radix-ui/react-use-callback-ref": "1.1.1",
|
| 1206 |
+
"@radix-ui/react-use-is-hydrated": "0.1.0",
|
| 1207 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 1208 |
+
},
|
| 1209 |
+
"peerDependencies": {
|
| 1210 |
+
"@types/react": "*",
|
| 1211 |
+
"@types/react-dom": "*",
|
| 1212 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1213 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1214 |
+
},
|
| 1215 |
+
"peerDependenciesMeta": {
|
| 1216 |
+
"@types/react": {
|
| 1217 |
+
"optional": true
|
| 1218 |
+
},
|
| 1219 |
+
"@types/react-dom": {
|
| 1220 |
+
"optional": true
|
| 1221 |
+
}
|
| 1222 |
+
}
|
| 1223 |
+
},
|
| 1224 |
+
"node_modules/@radix-ui/react-collection": {
|
| 1225 |
+
"version": "1.1.7",
|
| 1226 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
|
| 1227 |
+
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
|
| 1228 |
+
"license": "MIT",
|
| 1229 |
+
"dependencies": {
|
| 1230 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1231 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1232 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1233 |
+
"@radix-ui/react-slot": "1.2.3"
|
| 1234 |
+
},
|
| 1235 |
+
"peerDependencies": {
|
| 1236 |
+
"@types/react": "*",
|
| 1237 |
+
"@types/react-dom": "*",
|
| 1238 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1239 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1240 |
+
},
|
| 1241 |
+
"peerDependenciesMeta": {
|
| 1242 |
+
"@types/react": {
|
| 1243 |
+
"optional": true
|
| 1244 |
+
},
|
| 1245 |
+
"@types/react-dom": {
|
| 1246 |
+
"optional": true
|
| 1247 |
+
}
|
| 1248 |
+
}
|
| 1249 |
+
},
|
| 1250 |
+
"node_modules/@radix-ui/react-compose-refs": {
|
| 1251 |
+
"version": "1.1.2",
|
| 1252 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
| 1253 |
+
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
| 1254 |
+
"license": "MIT",
|
| 1255 |
+
"peerDependencies": {
|
| 1256 |
+
"@types/react": "*",
|
| 1257 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1258 |
+
},
|
| 1259 |
+
"peerDependenciesMeta": {
|
| 1260 |
+
"@types/react": {
|
| 1261 |
+
"optional": true
|
| 1262 |
+
}
|
| 1263 |
+
}
|
| 1264 |
+
},
|
| 1265 |
+
"node_modules/@radix-ui/react-context": {
|
| 1266 |
+
"version": "1.1.2",
|
| 1267 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
| 1268 |
+
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
| 1269 |
+
"license": "MIT",
|
| 1270 |
+
"peerDependencies": {
|
| 1271 |
+
"@types/react": "*",
|
| 1272 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1273 |
+
},
|
| 1274 |
+
"peerDependenciesMeta": {
|
| 1275 |
+
"@types/react": {
|
| 1276 |
+
"optional": true
|
| 1277 |
+
}
|
| 1278 |
+
}
|
| 1279 |
+
},
|
| 1280 |
+
"node_modules/@radix-ui/react-direction": {
|
| 1281 |
+
"version": "1.1.1",
|
| 1282 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
| 1283 |
+
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
| 1284 |
+
"license": "MIT",
|
| 1285 |
+
"peerDependencies": {
|
| 1286 |
+
"@types/react": "*",
|
| 1287 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1288 |
+
},
|
| 1289 |
+
"peerDependenciesMeta": {
|
| 1290 |
+
"@types/react": {
|
| 1291 |
+
"optional": true
|
| 1292 |
+
}
|
| 1293 |
+
}
|
| 1294 |
+
},
|
| 1295 |
+
"node_modules/@radix-ui/react-dismissable-layer": {
|
| 1296 |
+
"version": "1.1.10",
|
| 1297 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
|
| 1298 |
+
"integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==",
|
| 1299 |
+
"license": "MIT",
|
| 1300 |
+
"dependencies": {
|
| 1301 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1302 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1303 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1304 |
+
"@radix-ui/react-use-callback-ref": "1.1.1",
|
| 1305 |
+
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
| 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-dropdown-menu": {
|
| 1323 |
+
"version": "2.1.15",
|
| 1324 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz",
|
| 1325 |
+
"integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==",
|
| 1326 |
+
"license": "MIT",
|
| 1327 |
+
"dependencies": {
|
| 1328 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1329 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1330 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1331 |
+
"@radix-ui/react-id": "1.1.1",
|
| 1332 |
+
"@radix-ui/react-menu": "2.1.15",
|
| 1333 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1334 |
+
"@radix-ui/react-use-controllable-state": "1.2.2"
|
| 1335 |
+
},
|
| 1336 |
+
"peerDependencies": {
|
| 1337 |
+
"@types/react": "*",
|
| 1338 |
+
"@types/react-dom": "*",
|
| 1339 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1340 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1341 |
+
},
|
| 1342 |
+
"peerDependenciesMeta": {
|
| 1343 |
+
"@types/react": {
|
| 1344 |
+
"optional": true
|
| 1345 |
+
},
|
| 1346 |
+
"@types/react-dom": {
|
| 1347 |
+
"optional": true
|
| 1348 |
+
}
|
| 1349 |
+
}
|
| 1350 |
+
},
|
| 1351 |
+
"node_modules/@radix-ui/react-focus-guards": {
|
| 1352 |
+
"version": "1.1.2",
|
| 1353 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
|
| 1354 |
+
"integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==",
|
| 1355 |
+
"license": "MIT",
|
| 1356 |
+
"peerDependencies": {
|
| 1357 |
+
"@types/react": "*",
|
| 1358 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1359 |
+
},
|
| 1360 |
+
"peerDependenciesMeta": {
|
| 1361 |
+
"@types/react": {
|
| 1362 |
+
"optional": true
|
| 1363 |
+
}
|
| 1364 |
+
}
|
| 1365 |
+
},
|
| 1366 |
+
"node_modules/@radix-ui/react-focus-scope": {
|
| 1367 |
+
"version": "1.1.7",
|
| 1368 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
| 1369 |
+
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
| 1370 |
+
"license": "MIT",
|
| 1371 |
+
"dependencies": {
|
| 1372 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1373 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1374 |
+
"@radix-ui/react-use-callback-ref": "1.1.1"
|
| 1375 |
+
},
|
| 1376 |
+
"peerDependencies": {
|
| 1377 |
+
"@types/react": "*",
|
| 1378 |
+
"@types/react-dom": "*",
|
| 1379 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1380 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1381 |
+
},
|
| 1382 |
+
"peerDependenciesMeta": {
|
| 1383 |
+
"@types/react": {
|
| 1384 |
+
"optional": true
|
| 1385 |
+
},
|
| 1386 |
+
"@types/react-dom": {
|
| 1387 |
+
"optional": true
|
| 1388 |
+
}
|
| 1389 |
+
}
|
| 1390 |
+
},
|
| 1391 |
+
"node_modules/@radix-ui/react-id": {
|
| 1392 |
+
"version": "1.1.1",
|
| 1393 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
| 1394 |
+
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
| 1395 |
+
"license": "MIT",
|
| 1396 |
+
"dependencies": {
|
| 1397 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 1398 |
+
},
|
| 1399 |
+
"peerDependencies": {
|
| 1400 |
+
"@types/react": "*",
|
| 1401 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1402 |
+
},
|
| 1403 |
+
"peerDependenciesMeta": {
|
| 1404 |
+
"@types/react": {
|
| 1405 |
+
"optional": true
|
| 1406 |
+
}
|
| 1407 |
+
}
|
| 1408 |
+
},
|
| 1409 |
+
"node_modules/@radix-ui/react-menu": {
|
| 1410 |
+
"version": "2.1.15",
|
| 1411 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz",
|
| 1412 |
+
"integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==",
|
| 1413 |
+
"license": "MIT",
|
| 1414 |
+
"dependencies": {
|
| 1415 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1416 |
+
"@radix-ui/react-collection": "1.1.7",
|
| 1417 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1418 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1419 |
+
"@radix-ui/react-direction": "1.1.1",
|
| 1420 |
+
"@radix-ui/react-dismissable-layer": "1.1.10",
|
| 1421 |
+
"@radix-ui/react-focus-guards": "1.1.2",
|
| 1422 |
+
"@radix-ui/react-focus-scope": "1.1.7",
|
| 1423 |
+
"@radix-ui/react-id": "1.1.1",
|
| 1424 |
+
"@radix-ui/react-popper": "1.2.7",
|
| 1425 |
+
"@radix-ui/react-portal": "1.1.9",
|
| 1426 |
+
"@radix-ui/react-presence": "1.1.4",
|
| 1427 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1428 |
+
"@radix-ui/react-roving-focus": "1.1.10",
|
| 1429 |
+
"@radix-ui/react-slot": "1.2.3",
|
| 1430 |
+
"@radix-ui/react-use-callback-ref": "1.1.1",
|
| 1431 |
+
"aria-hidden": "^1.2.4",
|
| 1432 |
+
"react-remove-scroll": "^2.6.3"
|
| 1433 |
+
},
|
| 1434 |
+
"peerDependencies": {
|
| 1435 |
+
"@types/react": "*",
|
| 1436 |
+
"@types/react-dom": "*",
|
| 1437 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1438 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1439 |
+
},
|
| 1440 |
+
"peerDependenciesMeta": {
|
| 1441 |
+
"@types/react": {
|
| 1442 |
+
"optional": true
|
| 1443 |
+
},
|
| 1444 |
+
"@types/react-dom": {
|
| 1445 |
+
"optional": true
|
| 1446 |
+
}
|
| 1447 |
+
}
|
| 1448 |
+
},
|
| 1449 |
+
"node_modules/@radix-ui/react-popover": {
|
| 1450 |
+
"version": "1.1.14",
|
| 1451 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz",
|
| 1452 |
+
"integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==",
|
| 1453 |
+
"license": "MIT",
|
| 1454 |
+
"dependencies": {
|
| 1455 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1456 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1457 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1458 |
+
"@radix-ui/react-dismissable-layer": "1.1.10",
|
| 1459 |
+
"@radix-ui/react-focus-guards": "1.1.2",
|
| 1460 |
+
"@radix-ui/react-focus-scope": "1.1.7",
|
| 1461 |
+
"@radix-ui/react-id": "1.1.1",
|
| 1462 |
+
"@radix-ui/react-popper": "1.2.7",
|
| 1463 |
+
"@radix-ui/react-portal": "1.1.9",
|
| 1464 |
+
"@radix-ui/react-presence": "1.1.4",
|
| 1465 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1466 |
+
"@radix-ui/react-slot": "1.2.3",
|
| 1467 |
+
"@radix-ui/react-use-controllable-state": "1.2.2",
|
| 1468 |
+
"aria-hidden": "^1.2.4",
|
| 1469 |
+
"react-remove-scroll": "^2.6.3"
|
| 1470 |
+
},
|
| 1471 |
+
"peerDependencies": {
|
| 1472 |
+
"@types/react": "*",
|
| 1473 |
+
"@types/react-dom": "*",
|
| 1474 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1475 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1476 |
+
},
|
| 1477 |
+
"peerDependenciesMeta": {
|
| 1478 |
+
"@types/react": {
|
| 1479 |
+
"optional": true
|
| 1480 |
+
},
|
| 1481 |
+
"@types/react-dom": {
|
| 1482 |
+
"optional": true
|
| 1483 |
+
}
|
| 1484 |
+
}
|
| 1485 |
+
},
|
| 1486 |
+
"node_modules/@radix-ui/react-popper": {
|
| 1487 |
+
"version": "1.2.7",
|
| 1488 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
|
| 1489 |
+
"integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
|
| 1490 |
+
"license": "MIT",
|
| 1491 |
+
"dependencies": {
|
| 1492 |
+
"@floating-ui/react-dom": "^2.0.0",
|
| 1493 |
+
"@radix-ui/react-arrow": "1.1.7",
|
| 1494 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1495 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1496 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1497 |
+
"@radix-ui/react-use-callback-ref": "1.1.1",
|
| 1498 |
+
"@radix-ui/react-use-layout-effect": "1.1.1",
|
| 1499 |
+
"@radix-ui/react-use-rect": "1.1.1",
|
| 1500 |
+
"@radix-ui/react-use-size": "1.1.1",
|
| 1501 |
+
"@radix-ui/rect": "1.1.1"
|
| 1502 |
+
},
|
| 1503 |
+
"peerDependencies": {
|
| 1504 |
+
"@types/react": "*",
|
| 1505 |
+
"@types/react-dom": "*",
|
| 1506 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1507 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1508 |
+
},
|
| 1509 |
+
"peerDependenciesMeta": {
|
| 1510 |
+
"@types/react": {
|
| 1511 |
+
"optional": true
|
| 1512 |
+
},
|
| 1513 |
+
"@types/react-dom": {
|
| 1514 |
+
"optional": true
|
| 1515 |
+
}
|
| 1516 |
+
}
|
| 1517 |
+
},
|
| 1518 |
+
"node_modules/@radix-ui/react-portal": {
|
| 1519 |
+
"version": "1.1.9",
|
| 1520 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
| 1521 |
+
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
| 1522 |
+
"license": "MIT",
|
| 1523 |
+
"dependencies": {
|
| 1524 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1525 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 1526 |
+
},
|
| 1527 |
+
"peerDependencies": {
|
| 1528 |
+
"@types/react": "*",
|
| 1529 |
+
"@types/react-dom": "*",
|
| 1530 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1531 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1532 |
+
},
|
| 1533 |
+
"peerDependenciesMeta": {
|
| 1534 |
+
"@types/react": {
|
| 1535 |
+
"optional": true
|
| 1536 |
+
},
|
| 1537 |
+
"@types/react-dom": {
|
| 1538 |
+
"optional": true
|
| 1539 |
+
}
|
| 1540 |
+
}
|
| 1541 |
+
},
|
| 1542 |
+
"node_modules/@radix-ui/react-presence": {
|
| 1543 |
+
"version": "1.1.4",
|
| 1544 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
|
| 1545 |
+
"integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
|
| 1546 |
+
"license": "MIT",
|
| 1547 |
+
"dependencies": {
|
| 1548 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1549 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 1550 |
+
},
|
| 1551 |
+
"peerDependencies": {
|
| 1552 |
+
"@types/react": "*",
|
| 1553 |
+
"@types/react-dom": "*",
|
| 1554 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1555 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1556 |
+
},
|
| 1557 |
+
"peerDependenciesMeta": {
|
| 1558 |
+
"@types/react": {
|
| 1559 |
+
"optional": true
|
| 1560 |
+
},
|
| 1561 |
+
"@types/react-dom": {
|
| 1562 |
+
"optional": true
|
| 1563 |
+
}
|
| 1564 |
+
}
|
| 1565 |
+
},
|
| 1566 |
+
"node_modules/@radix-ui/react-primitive": {
|
| 1567 |
+
"version": "2.1.3",
|
| 1568 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
| 1569 |
+
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
| 1570 |
+
"license": "MIT",
|
| 1571 |
+
"dependencies": {
|
| 1572 |
+
"@radix-ui/react-slot": "1.2.3"
|
| 1573 |
+
},
|
| 1574 |
+
"peerDependencies": {
|
| 1575 |
+
"@types/react": "*",
|
| 1576 |
+
"@types/react-dom": "*",
|
| 1577 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1578 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1579 |
+
},
|
| 1580 |
+
"peerDependenciesMeta": {
|
| 1581 |
+
"@types/react": {
|
| 1582 |
+
"optional": true
|
| 1583 |
+
},
|
| 1584 |
+
"@types/react-dom": {
|
| 1585 |
+
"optional": true
|
| 1586 |
+
}
|
| 1587 |
+
}
|
| 1588 |
+
},
|
| 1589 |
+
"node_modules/@radix-ui/react-roving-focus": {
|
| 1590 |
+
"version": "1.1.10",
|
| 1591 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz",
|
| 1592 |
+
"integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==",
|
| 1593 |
+
"license": "MIT",
|
| 1594 |
+
"dependencies": {
|
| 1595 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1596 |
+
"@radix-ui/react-collection": "1.1.7",
|
| 1597 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1598 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1599 |
+
"@radix-ui/react-direction": "1.1.1",
|
| 1600 |
+
"@radix-ui/react-id": "1.1.1",
|
| 1601 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1602 |
+
"@radix-ui/react-use-callback-ref": "1.1.1",
|
| 1603 |
+
"@radix-ui/react-use-controllable-state": "1.2.2"
|
| 1604 |
+
},
|
| 1605 |
+
"peerDependencies": {
|
| 1606 |
+
"@types/react": "*",
|
| 1607 |
+
"@types/react-dom": "*",
|
| 1608 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1609 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1610 |
+
},
|
| 1611 |
+
"peerDependenciesMeta": {
|
| 1612 |
+
"@types/react": {
|
| 1613 |
+
"optional": true
|
| 1614 |
+
},
|
| 1615 |
+
"@types/react-dom": {
|
| 1616 |
+
"optional": true
|
| 1617 |
+
}
|
| 1618 |
+
}
|
| 1619 |
+
},
|
| 1620 |
+
"node_modules/@radix-ui/react-select": {
|
| 1621 |
+
"version": "2.2.5",
|
| 1622 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz",
|
| 1623 |
+
"integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==",
|
| 1624 |
+
"license": "MIT",
|
| 1625 |
+
"dependencies": {
|
| 1626 |
+
"@radix-ui/number": "1.1.1",
|
| 1627 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1628 |
+
"@radix-ui/react-collection": "1.1.7",
|
| 1629 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1630 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1631 |
+
"@radix-ui/react-direction": "1.1.1",
|
| 1632 |
+
"@radix-ui/react-dismissable-layer": "1.1.10",
|
| 1633 |
+
"@radix-ui/react-focus-guards": "1.1.2",
|
| 1634 |
+
"@radix-ui/react-focus-scope": "1.1.7",
|
| 1635 |
+
"@radix-ui/react-id": "1.1.1",
|
| 1636 |
+
"@radix-ui/react-popper": "1.2.7",
|
| 1637 |
+
"@radix-ui/react-portal": "1.1.9",
|
| 1638 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1639 |
+
"@radix-ui/react-slot": "1.2.3",
|
| 1640 |
+
"@radix-ui/react-use-callback-ref": "1.1.1",
|
| 1641 |
+
"@radix-ui/react-use-controllable-state": "1.2.2",
|
| 1642 |
+
"@radix-ui/react-use-layout-effect": "1.1.1",
|
| 1643 |
+
"@radix-ui/react-use-previous": "1.1.1",
|
| 1644 |
+
"@radix-ui/react-visually-hidden": "1.2.3",
|
| 1645 |
+
"aria-hidden": "^1.2.4",
|
| 1646 |
+
"react-remove-scroll": "^2.6.3"
|
| 1647 |
+
},
|
| 1648 |
+
"peerDependencies": {
|
| 1649 |
+
"@types/react": "*",
|
| 1650 |
+
"@types/react-dom": "*",
|
| 1651 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1652 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1653 |
+
},
|
| 1654 |
+
"peerDependenciesMeta": {
|
| 1655 |
+
"@types/react": {
|
| 1656 |
+
"optional": true
|
| 1657 |
+
},
|
| 1658 |
+
"@types/react-dom": {
|
| 1659 |
+
"optional": true
|
| 1660 |
+
}
|
| 1661 |
+
}
|
| 1662 |
+
},
|
| 1663 |
+
"node_modules/@radix-ui/react-slot": {
|
| 1664 |
+
"version": "1.2.3",
|
| 1665 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
| 1666 |
+
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
| 1667 |
+
"license": "MIT",
|
| 1668 |
+
"dependencies": {
|
| 1669 |
+
"@radix-ui/react-compose-refs": "1.1.2"
|
| 1670 |
+
},
|
| 1671 |
+
"peerDependencies": {
|
| 1672 |
+
"@types/react": "*",
|
| 1673 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1674 |
+
},
|
| 1675 |
+
"peerDependenciesMeta": {
|
| 1676 |
+
"@types/react": {
|
| 1677 |
+
"optional": true
|
| 1678 |
+
}
|
| 1679 |
+
}
|
| 1680 |
+
},
|
| 1681 |
+
"node_modules/@radix-ui/react-switch": {
|
| 1682 |
+
"version": "1.2.5",
|
| 1683 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz",
|
| 1684 |
+
"integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==",
|
| 1685 |
+
"license": "MIT",
|
| 1686 |
+
"dependencies": {
|
| 1687 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1688 |
+
"@radix-ui/react-compose-refs": "1.1.2",
|
| 1689 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1690 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1691 |
+
"@radix-ui/react-use-controllable-state": "1.2.2",
|
| 1692 |
+
"@radix-ui/react-use-previous": "1.1.1",
|
| 1693 |
+
"@radix-ui/react-use-size": "1.1.1"
|
| 1694 |
+
},
|
| 1695 |
+
"peerDependencies": {
|
| 1696 |
+
"@types/react": "*",
|
| 1697 |
+
"@types/react-dom": "*",
|
| 1698 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1699 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1700 |
+
},
|
| 1701 |
+
"peerDependenciesMeta": {
|
| 1702 |
+
"@types/react": {
|
| 1703 |
+
"optional": true
|
| 1704 |
+
},
|
| 1705 |
+
"@types/react-dom": {
|
| 1706 |
+
"optional": true
|
| 1707 |
+
}
|
| 1708 |
+
}
|
| 1709 |
+
},
|
| 1710 |
+
"node_modules/@radix-ui/react-tabs": {
|
| 1711 |
+
"version": "1.1.12",
|
| 1712 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz",
|
| 1713 |
+
"integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==",
|
| 1714 |
+
"license": "MIT",
|
| 1715 |
+
"dependencies": {
|
| 1716 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1717 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1718 |
+
"@radix-ui/react-direction": "1.1.1",
|
| 1719 |
+
"@radix-ui/react-id": "1.1.1",
|
| 1720 |
+
"@radix-ui/react-presence": "1.1.4",
|
| 1721 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1722 |
+
"@radix-ui/react-roving-focus": "1.1.10",
|
| 1723 |
+
"@radix-ui/react-use-controllable-state": "1.2.2"
|
| 1724 |
+
},
|
| 1725 |
+
"peerDependencies": {
|
| 1726 |
+
"@types/react": "*",
|
| 1727 |
+
"@types/react-dom": "*",
|
| 1728 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1729 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1730 |
+
},
|
| 1731 |
+
"peerDependenciesMeta": {
|
| 1732 |
+
"@types/react": {
|
| 1733 |
+
"optional": true
|
| 1734 |
+
},
|
| 1735 |
+
"@types/react-dom": {
|
| 1736 |
+
"optional": true
|
| 1737 |
+
}
|
| 1738 |
+
}
|
| 1739 |
+
},
|
| 1740 |
+
"node_modules/@radix-ui/react-toggle": {
|
| 1741 |
+
"version": "1.1.9",
|
| 1742 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.9.tgz",
|
| 1743 |
+
"integrity": "sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==",
|
| 1744 |
+
"license": "MIT",
|
| 1745 |
+
"dependencies": {
|
| 1746 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1747 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1748 |
+
"@radix-ui/react-use-controllable-state": "1.2.2"
|
| 1749 |
+
},
|
| 1750 |
+
"peerDependencies": {
|
| 1751 |
+
"@types/react": "*",
|
| 1752 |
+
"@types/react-dom": "*",
|
| 1753 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1754 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1755 |
+
},
|
| 1756 |
+
"peerDependenciesMeta": {
|
| 1757 |
+
"@types/react": {
|
| 1758 |
+
"optional": true
|
| 1759 |
+
},
|
| 1760 |
+
"@types/react-dom": {
|
| 1761 |
+
"optional": true
|
| 1762 |
+
}
|
| 1763 |
+
}
|
| 1764 |
+
},
|
| 1765 |
+
"node_modules/@radix-ui/react-toggle-group": {
|
| 1766 |
+
"version": "1.1.10",
|
| 1767 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.10.tgz",
|
| 1768 |
+
"integrity": "sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==",
|
| 1769 |
+
"license": "MIT",
|
| 1770 |
+
"dependencies": {
|
| 1771 |
+
"@radix-ui/primitive": "1.1.2",
|
| 1772 |
+
"@radix-ui/react-context": "1.1.2",
|
| 1773 |
+
"@radix-ui/react-direction": "1.1.1",
|
| 1774 |
+
"@radix-ui/react-primitive": "2.1.3",
|
| 1775 |
+
"@radix-ui/react-roving-focus": "1.1.10",
|
| 1776 |
+
"@radix-ui/react-toggle": "1.1.9",
|
| 1777 |
+
"@radix-ui/react-use-controllable-state": "1.2.2"
|
| 1778 |
+
},
|
| 1779 |
+
"peerDependencies": {
|
| 1780 |
+
"@types/react": "*",
|
| 1781 |
+
"@types/react-dom": "*",
|
| 1782 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1783 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1784 |
+
},
|
| 1785 |
+
"peerDependenciesMeta": {
|
| 1786 |
+
"@types/react": {
|
| 1787 |
+
"optional": true
|
| 1788 |
+
},
|
| 1789 |
+
"@types/react-dom": {
|
| 1790 |
+
"optional": true
|
| 1791 |
+
}
|
| 1792 |
+
}
|
| 1793 |
+
},
|
| 1794 |
+
"node_modules/@radix-ui/react-use-callback-ref": {
|
| 1795 |
+
"version": "1.1.1",
|
| 1796 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
| 1797 |
+
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
| 1798 |
+
"license": "MIT",
|
| 1799 |
+
"peerDependencies": {
|
| 1800 |
+
"@types/react": "*",
|
| 1801 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1802 |
+
},
|
| 1803 |
+
"peerDependenciesMeta": {
|
| 1804 |
+
"@types/react": {
|
| 1805 |
+
"optional": true
|
| 1806 |
+
}
|
| 1807 |
+
}
|
| 1808 |
+
},
|
| 1809 |
+
"node_modules/@radix-ui/react-use-controllable-state": {
|
| 1810 |
+
"version": "1.2.2",
|
| 1811 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
| 1812 |
+
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
| 1813 |
+
"license": "MIT",
|
| 1814 |
+
"dependencies": {
|
| 1815 |
+
"@radix-ui/react-use-effect-event": "0.0.2",
|
| 1816 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 1817 |
+
},
|
| 1818 |
+
"peerDependencies": {
|
| 1819 |
+
"@types/react": "*",
|
| 1820 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1821 |
+
},
|
| 1822 |
+
"peerDependenciesMeta": {
|
| 1823 |
+
"@types/react": {
|
| 1824 |
+
"optional": true
|
| 1825 |
+
}
|
| 1826 |
+
}
|
| 1827 |
+
},
|
| 1828 |
+
"node_modules/@radix-ui/react-use-effect-event": {
|
| 1829 |
+
"version": "0.0.2",
|
| 1830 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
| 1831 |
+
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
| 1832 |
+
"license": "MIT",
|
| 1833 |
+
"dependencies": {
|
| 1834 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 1835 |
+
},
|
| 1836 |
+
"peerDependencies": {
|
| 1837 |
+
"@types/react": "*",
|
| 1838 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1839 |
+
},
|
| 1840 |
+
"peerDependenciesMeta": {
|
| 1841 |
+
"@types/react": {
|
| 1842 |
+
"optional": true
|
| 1843 |
+
}
|
| 1844 |
+
}
|
| 1845 |
+
},
|
| 1846 |
+
"node_modules/@radix-ui/react-use-escape-keydown": {
|
| 1847 |
+
"version": "1.1.1",
|
| 1848 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
| 1849 |
+
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
| 1850 |
+
"license": "MIT",
|
| 1851 |
+
"dependencies": {
|
| 1852 |
+
"@radix-ui/react-use-callback-ref": "1.1.1"
|
| 1853 |
+
},
|
| 1854 |
+
"peerDependencies": {
|
| 1855 |
+
"@types/react": "*",
|
| 1856 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1857 |
+
},
|
| 1858 |
+
"peerDependenciesMeta": {
|
| 1859 |
+
"@types/react": {
|
| 1860 |
+
"optional": true
|
| 1861 |
+
}
|
| 1862 |
+
}
|
| 1863 |
+
},
|
| 1864 |
+
"node_modules/@radix-ui/react-use-is-hydrated": {
|
| 1865 |
+
"version": "0.1.0",
|
| 1866 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz",
|
| 1867 |
+
"integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==",
|
| 1868 |
+
"license": "MIT",
|
| 1869 |
+
"dependencies": {
|
| 1870 |
+
"use-sync-external-store": "^1.5.0"
|
| 1871 |
+
},
|
| 1872 |
+
"peerDependencies": {
|
| 1873 |
+
"@types/react": "*",
|
| 1874 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1875 |
+
},
|
| 1876 |
+
"peerDependenciesMeta": {
|
| 1877 |
+
"@types/react": {
|
| 1878 |
+
"optional": true
|
| 1879 |
+
}
|
| 1880 |
+
}
|
| 1881 |
+
},
|
| 1882 |
+
"node_modules/@radix-ui/react-use-layout-effect": {
|
| 1883 |
+
"version": "1.1.1",
|
| 1884 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
| 1885 |
+
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
| 1886 |
+
"license": "MIT",
|
| 1887 |
+
"peerDependencies": {
|
| 1888 |
+
"@types/react": "*",
|
| 1889 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1890 |
+
},
|
| 1891 |
+
"peerDependenciesMeta": {
|
| 1892 |
+
"@types/react": {
|
| 1893 |
+
"optional": true
|
| 1894 |
+
}
|
| 1895 |
+
}
|
| 1896 |
+
},
|
| 1897 |
+
"node_modules/@radix-ui/react-use-previous": {
|
| 1898 |
+
"version": "1.1.1",
|
| 1899 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
|
| 1900 |
+
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
|
| 1901 |
+
"license": "MIT",
|
| 1902 |
+
"peerDependencies": {
|
| 1903 |
+
"@types/react": "*",
|
| 1904 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1905 |
+
},
|
| 1906 |
+
"peerDependenciesMeta": {
|
| 1907 |
+
"@types/react": {
|
| 1908 |
+
"optional": true
|
| 1909 |
+
}
|
| 1910 |
+
}
|
| 1911 |
+
},
|
| 1912 |
+
"node_modules/@radix-ui/react-use-rect": {
|
| 1913 |
+
"version": "1.1.1",
|
| 1914 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
|
| 1915 |
+
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
|
| 1916 |
+
"license": "MIT",
|
| 1917 |
+
"dependencies": {
|
| 1918 |
+
"@radix-ui/rect": "1.1.1"
|
| 1919 |
+
},
|
| 1920 |
+
"peerDependencies": {
|
| 1921 |
+
"@types/react": "*",
|
| 1922 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1923 |
+
},
|
| 1924 |
+
"peerDependenciesMeta": {
|
| 1925 |
+
"@types/react": {
|
| 1926 |
+
"optional": true
|
| 1927 |
+
}
|
| 1928 |
+
}
|
| 1929 |
+
},
|
| 1930 |
+
"node_modules/@radix-ui/react-use-size": {
|
| 1931 |
+
"version": "1.1.1",
|
| 1932 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
|
| 1933 |
+
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
|
| 1934 |
+
"license": "MIT",
|
| 1935 |
+
"dependencies": {
|
| 1936 |
+
"@radix-ui/react-use-layout-effect": "1.1.1"
|
| 1937 |
+
},
|
| 1938 |
+
"peerDependencies": {
|
| 1939 |
+
"@types/react": "*",
|
| 1940 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1941 |
+
},
|
| 1942 |
+
"peerDependenciesMeta": {
|
| 1943 |
+
"@types/react": {
|
| 1944 |
+
"optional": true
|
| 1945 |
+
}
|
| 1946 |
+
}
|
| 1947 |
+
},
|
| 1948 |
+
"node_modules/@radix-ui/react-visually-hidden": {
|
| 1949 |
+
"version": "1.2.3",
|
| 1950 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
|
| 1951 |
+
"integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
|
| 1952 |
+
"license": "MIT",
|
| 1953 |
+
"dependencies": {
|
| 1954 |
+
"@radix-ui/react-primitive": "2.1.3"
|
| 1955 |
+
},
|
| 1956 |
+
"peerDependencies": {
|
| 1957 |
+
"@types/react": "*",
|
| 1958 |
+
"@types/react-dom": "*",
|
| 1959 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
| 1960 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
| 1961 |
+
},
|
| 1962 |
+
"peerDependenciesMeta": {
|
| 1963 |
+
"@types/react": {
|
| 1964 |
+
"optional": true
|
| 1965 |
+
},
|
| 1966 |
+
"@types/react-dom": {
|
| 1967 |
+
"optional": true
|
| 1968 |
+
}
|
| 1969 |
+
}
|
| 1970 |
+
},
|
| 1971 |
+
"node_modules/@radix-ui/rect": {
|
| 1972 |
+
"version": "1.1.1",
|
| 1973 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
| 1974 |
+
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
| 1975 |
+
"license": "MIT"
|
| 1976 |
+
},
|
| 1977 |
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 1978 |
"version": "4.36.0",
|
| 1979 |
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz",
|
|
|
|
| 2572 |
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
|
| 2573 |
},
|
| 2574 |
"node_modules/@types/node": {
|
| 2575 |
+
"version": "22.15.21",
|
| 2576 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
| 2577 |
+
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
| 2578 |
+
"license": "MIT",
|
| 2579 |
"dependencies": {
|
| 2580 |
+
"undici-types": "~6.21.0"
|
| 2581 |
}
|
| 2582 |
},
|
| 2583 |
"node_modules/@types/qs": {
|
|
|
|
| 2604 |
"version": "19.0.4",
|
| 2605 |
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
|
| 2606 |
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
|
| 2607 |
+
"devOptional": true,
|
| 2608 |
"peerDependencies": {
|
| 2609 |
"@types/react": "^19.0.0"
|
| 2610 |
}
|
|
|
|
| 2972 |
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
| 2973 |
"dev": true
|
| 2974 |
},
|
| 2975 |
+
"node_modules/aria-hidden": {
|
| 2976 |
+
"version": "1.2.6",
|
| 2977 |
+
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
| 2978 |
+
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
| 2979 |
+
"license": "MIT",
|
| 2980 |
+
"dependencies": {
|
| 2981 |
+
"tslib": "^2.0.0"
|
| 2982 |
+
},
|
| 2983 |
+
"engines": {
|
| 2984 |
+
"node": ">=10"
|
| 2985 |
+
}
|
| 2986 |
+
},
|
| 2987 |
"node_modules/array-flatten": {
|
| 2988 |
"version": "1.1.1",
|
| 2989 |
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
|
|
|
| 3348 |
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
| 3349 |
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
| 3350 |
},
|
| 3351 |
+
"node_modules/class-variance-authority": {
|
| 3352 |
+
"version": "0.7.1",
|
| 3353 |
+
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
| 3354 |
+
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
| 3355 |
+
"license": "Apache-2.0",
|
| 3356 |
+
"dependencies": {
|
| 3357 |
+
"clsx": "^2.1.1"
|
| 3358 |
+
},
|
| 3359 |
+
"funding": {
|
| 3360 |
+
"url": "https://polar.sh/cva"
|
| 3361 |
+
}
|
| 3362 |
+
},
|
| 3363 |
"node_modules/classnames": {
|
| 3364 |
"version": "2.5.1",
|
| 3365 |
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
|
|
|
| 3369 |
"version": "2.1.1",
|
| 3370 |
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
| 3371 |
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
| 3372 |
+
"license": "MIT",
|
| 3373 |
"engines": {
|
| 3374 |
"node": ">=6"
|
| 3375 |
}
|
|
|
|
| 3620 |
"node": ">=8"
|
| 3621 |
}
|
| 3622 |
},
|
| 3623 |
+
"node_modules/detect-node-es": {
|
| 3624 |
+
"version": "1.1.0",
|
| 3625 |
+
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
| 3626 |
+
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
| 3627 |
+
"license": "MIT"
|
| 3628 |
+
},
|
| 3629 |
"node_modules/devlop": {
|
| 3630 |
"version": "1.1.0",
|
| 3631 |
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
|
|
|
|
| 4294 |
"url": "https://github.com/sponsors/ljharb"
|
| 4295 |
}
|
| 4296 |
},
|
| 4297 |
+
"node_modules/get-nonce": {
|
| 4298 |
+
"version": "1.0.1",
|
| 4299 |
+
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
| 4300 |
+
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
| 4301 |
+
"license": "MIT",
|
| 4302 |
+
"engines": {
|
| 4303 |
+
"node": ">=6"
|
| 4304 |
+
}
|
| 4305 |
+
},
|
| 4306 |
"node_modules/get-proto": {
|
| 4307 |
"version": "1.0.1",
|
| 4308 |
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
|
|
|
| 5012 |
"yallist": "^3.0.2"
|
| 5013 |
}
|
| 5014 |
},
|
| 5015 |
+
"node_modules/lucide-react": {
|
| 5016 |
+
"version": "0.511.0",
|
| 5017 |
+
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz",
|
| 5018 |
+
"integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==",
|
| 5019 |
+
"license": "ISC",
|
| 5020 |
+
"peerDependencies": {
|
| 5021 |
+
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
| 5022 |
+
}
|
| 5023 |
+
},
|
| 5024 |
"node_modules/math-intrinsics": {
|
| 5025 |
"version": "1.1.0",
|
| 5026 |
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
|
|
| 5778 |
"node": ">= 0.6"
|
| 5779 |
}
|
| 5780 |
},
|
| 5781 |
+
"node_modules/next-themes": {
|
| 5782 |
+
"version": "0.4.6",
|
| 5783 |
+
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
|
| 5784 |
+
"integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
|
| 5785 |
+
"license": "MIT",
|
| 5786 |
+
"peerDependencies": {
|
| 5787 |
+
"react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
|
| 5788 |
+
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
|
| 5789 |
+
}
|
| 5790 |
+
},
|
| 5791 |
"node_modules/node-abi": {
|
| 5792 |
"version": "3.74.0",
|
| 5793 |
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz",
|
|
|
|
| 6317 |
"node": ">=0.10.0"
|
| 6318 |
}
|
| 6319 |
},
|
| 6320 |
+
"node_modules/react-remove-scroll": {
|
| 6321 |
+
"version": "2.7.0",
|
| 6322 |
+
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.0.tgz",
|
| 6323 |
+
"integrity": "sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg==",
|
| 6324 |
+
"license": "MIT",
|
| 6325 |
+
"dependencies": {
|
| 6326 |
+
"react-remove-scroll-bar": "^2.3.7",
|
| 6327 |
+
"react-style-singleton": "^2.2.3",
|
| 6328 |
+
"tslib": "^2.1.0",
|
| 6329 |
+
"use-callback-ref": "^1.3.3",
|
| 6330 |
+
"use-sidecar": "^1.1.3"
|
| 6331 |
+
},
|
| 6332 |
+
"engines": {
|
| 6333 |
+
"node": ">=10"
|
| 6334 |
+
},
|
| 6335 |
+
"peerDependencies": {
|
| 6336 |
+
"@types/react": "*",
|
| 6337 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
| 6338 |
+
},
|
| 6339 |
+
"peerDependenciesMeta": {
|
| 6340 |
+
"@types/react": {
|
| 6341 |
+
"optional": true
|
| 6342 |
+
}
|
| 6343 |
+
}
|
| 6344 |
+
},
|
| 6345 |
+
"node_modules/react-remove-scroll-bar": {
|
| 6346 |
+
"version": "2.3.8",
|
| 6347 |
+
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
| 6348 |
+
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
| 6349 |
+
"license": "MIT",
|
| 6350 |
+
"dependencies": {
|
| 6351 |
+
"react-style-singleton": "^2.2.2",
|
| 6352 |
+
"tslib": "^2.0.0"
|
| 6353 |
+
},
|
| 6354 |
+
"engines": {
|
| 6355 |
+
"node": ">=10"
|
| 6356 |
+
},
|
| 6357 |
+
"peerDependencies": {
|
| 6358 |
+
"@types/react": "*",
|
| 6359 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
| 6360 |
+
},
|
| 6361 |
+
"peerDependenciesMeta": {
|
| 6362 |
+
"@types/react": {
|
| 6363 |
+
"optional": true
|
| 6364 |
+
}
|
| 6365 |
+
}
|
| 6366 |
+
},
|
| 6367 |
"node_modules/react-speech-recognition": {
|
| 6368 |
"version": "4.0.0",
|
| 6369 |
"resolved": "https://registry.npmjs.org/react-speech-recognition/-/react-speech-recognition-4.0.0.tgz",
|
|
|
|
| 6372 |
"react": ">=16.8.0"
|
| 6373 |
}
|
| 6374 |
},
|
| 6375 |
+
"node_modules/react-style-singleton": {
|
| 6376 |
+
"version": "2.2.3",
|
| 6377 |
+
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
| 6378 |
+
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
| 6379 |
+
"license": "MIT",
|
| 6380 |
+
"dependencies": {
|
| 6381 |
+
"get-nonce": "^1.0.0",
|
| 6382 |
+
"tslib": "^2.0.0"
|
| 6383 |
+
},
|
| 6384 |
+
"engines": {
|
| 6385 |
+
"node": ">=10"
|
| 6386 |
+
},
|
| 6387 |
+
"peerDependencies": {
|
| 6388 |
+
"@types/react": "*",
|
| 6389 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
| 6390 |
+
},
|
| 6391 |
+
"peerDependenciesMeta": {
|
| 6392 |
+
"@types/react": {
|
| 6393 |
+
"optional": true
|
| 6394 |
+
}
|
| 6395 |
+
}
|
| 6396 |
+
},
|
| 6397 |
"node_modules/react-toastify": {
|
| 6398 |
"version": "11.0.5",
|
| 6399 |
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
|
|
|
| 6874 |
"is-arrayish": "^0.3.1"
|
| 6875 |
}
|
| 6876 |
},
|
| 6877 |
+
"node_modules/sonner": {
|
| 6878 |
+
"version": "2.0.3",
|
| 6879 |
+
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz",
|
| 6880 |
+
"integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==",
|
| 6881 |
+
"license": "MIT",
|
| 6882 |
+
"peerDependencies": {
|
| 6883 |
+
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
| 6884 |
+
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
| 6885 |
+
}
|
| 6886 |
+
},
|
| 6887 |
"node_modules/source-map": {
|
| 6888 |
"version": "0.6.1",
|
| 6889 |
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
|
|
|
| 7040 |
"node": ">=8"
|
| 7041 |
}
|
| 7042 |
},
|
| 7043 |
+
"node_modules/tailwind-merge": {
|
| 7044 |
+
"version": "3.3.0",
|
| 7045 |
+
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.0.tgz",
|
| 7046 |
+
"integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==",
|
| 7047 |
+
"license": "MIT",
|
| 7048 |
+
"funding": {
|
| 7049 |
+
"type": "github",
|
| 7050 |
+
"url": "https://github.com/sponsors/dcastil"
|
| 7051 |
+
}
|
| 7052 |
+
},
|
| 7053 |
"node_modules/tailwindcss": {
|
| 7054 |
"version": "4.0.15",
|
| 7055 |
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz",
|
|
|
|
| 7178 |
"node": "*"
|
| 7179 |
}
|
| 7180 |
},
|
| 7181 |
+
"node_modules/tw-animate-css": {
|
| 7182 |
+
"version": "1.3.0",
|
| 7183 |
+
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.0.tgz",
|
| 7184 |
+
"integrity": "sha512-jrJ0XenzS9KVuDThJDvnhalbl4IYiMQ/XvpA0a2FL8KmlK+6CSMviO7ROY/I7z1NnUs5NnDhlM6fXmF40xPxzw==",
|
| 7185 |
+
"dev": true,
|
| 7186 |
+
"license": "MIT",
|
| 7187 |
+
"funding": {
|
| 7188 |
+
"url": "https://github.com/sponsors/Wombosvideo"
|
| 7189 |
+
}
|
| 7190 |
+
},
|
| 7191 |
"node_modules/type-check": {
|
| 7192 |
"version": "0.4.0",
|
| 7193 |
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
|
|
|
| 7248 |
}
|
| 7249 |
},
|
| 7250 |
"node_modules/undici-types": {
|
| 7251 |
+
"version": "6.21.0",
|
| 7252 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
| 7253 |
+
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
| 7254 |
+
"license": "MIT"
|
| 7255 |
},
|
| 7256 |
"node_modules/unified": {
|
| 7257 |
"version": "11.0.5",
|
|
|
|
| 7381 |
"punycode": "^2.1.0"
|
| 7382 |
}
|
| 7383 |
},
|
| 7384 |
+
"node_modules/use-callback-ref": {
|
| 7385 |
+
"version": "1.3.3",
|
| 7386 |
+
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
| 7387 |
+
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
| 7388 |
+
"license": "MIT",
|
| 7389 |
+
"dependencies": {
|
| 7390 |
+
"tslib": "^2.0.0"
|
| 7391 |
+
},
|
| 7392 |
+
"engines": {
|
| 7393 |
+
"node": ">=10"
|
| 7394 |
+
},
|
| 7395 |
+
"peerDependencies": {
|
| 7396 |
+
"@types/react": "*",
|
| 7397 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
| 7398 |
+
},
|
| 7399 |
+
"peerDependenciesMeta": {
|
| 7400 |
+
"@types/react": {
|
| 7401 |
+
"optional": true
|
| 7402 |
+
}
|
| 7403 |
+
}
|
| 7404 |
+
},
|
| 7405 |
+
"node_modules/use-sidecar": {
|
| 7406 |
+
"version": "1.1.3",
|
| 7407 |
+
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
| 7408 |
+
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
| 7409 |
+
"license": "MIT",
|
| 7410 |
+
"dependencies": {
|
| 7411 |
+
"detect-node-es": "^1.1.0",
|
| 7412 |
+
"tslib": "^2.0.0"
|
| 7413 |
+
},
|
| 7414 |
+
"engines": {
|
| 7415 |
+
"node": ">=10"
|
| 7416 |
+
},
|
| 7417 |
+
"peerDependencies": {
|
| 7418 |
+
"@types/react": "*",
|
| 7419 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
| 7420 |
+
},
|
| 7421 |
+
"peerDependenciesMeta": {
|
| 7422 |
+
"@types/react": {
|
| 7423 |
+
"optional": true
|
| 7424 |
+
}
|
| 7425 |
+
}
|
| 7426 |
+
},
|
| 7427 |
+
"node_modules/use-sync-external-store": {
|
| 7428 |
+
"version": "1.5.0",
|
| 7429 |
+
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
| 7430 |
+
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
| 7431 |
+
"license": "MIT",
|
| 7432 |
+
"peerDependencies": {
|
| 7433 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
| 7434 |
+
}
|
| 7435 |
+
},
|
| 7436 |
"node_modules/util-deprecate": {
|
| 7437 |
"version": "1.0.2",
|
| 7438 |
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
package.json
CHANGED
|
@@ -14,13 +14,26 @@
|
|
| 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,11 +41,14 @@
|
|
| 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,6 +57,7 @@
|
|
| 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"
|
|
|
|
| 14 |
"@huggingface/hub": "^1.1.1",
|
| 15 |
"@huggingface/inference": "^3.6.1",
|
| 16 |
"@monaco-editor/react": "^4.7.0",
|
| 17 |
+
"@radix-ui/react-avatar": "^1.1.10",
|
| 18 |
+
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
| 19 |
+
"@radix-ui/react-popover": "^1.1.14",
|
| 20 |
+
"@radix-ui/react-select": "^2.2.5",
|
| 21 |
+
"@radix-ui/react-slot": "^1.2.3",
|
| 22 |
+
"@radix-ui/react-switch": "^1.2.5",
|
| 23 |
+
"@radix-ui/react-tabs": "^1.1.12",
|
| 24 |
+
"@radix-ui/react-toggle": "^1.1.9",
|
| 25 |
+
"@radix-ui/react-toggle-group": "^1.1.10",
|
| 26 |
"@tailwindcss/vite": "^4.0.15",
|
| 27 |
"@xenova/transformers": "^2.17.2",
|
| 28 |
"body-parser": "^1.20.3",
|
| 29 |
+
"class-variance-authority": "^0.7.1",
|
| 30 |
"classnames": "^2.5.1",
|
| 31 |
+
"clsx": "^2.1.1",
|
| 32 |
"cookie-parser": "^1.4.7",
|
| 33 |
"dotenv": "^16.4.7",
|
| 34 |
"express": "^4.21.2",
|
| 35 |
+
"lucide-react": "^0.511.0",
|
| 36 |
+
"next-themes": "^0.4.6",
|
| 37 |
"react": "^19.0.0",
|
| 38 |
"react-dom": "^19.0.0",
|
| 39 |
"react-icons": "^5.5.0",
|
|
|
|
| 41 |
"react-speech-recognition": "^4.0.0",
|
| 42 |
"react-toastify": "^11.0.5",
|
| 43 |
"react-use": "^17.6.0",
|
| 44 |
+
"sonner": "^2.0.3",
|
| 45 |
+
"tailwind-merge": "^3.3.0",
|
| 46 |
"tailwindcss": "^4.0.15"
|
| 47 |
},
|
| 48 |
"devDependencies": {
|
| 49 |
"@eslint/js": "^9.21.0",
|
| 50 |
"@types/express": "^5.0.1",
|
| 51 |
+
"@types/node": "^22.15.21",
|
| 52 |
"@types/react": "^19.0.10",
|
| 53 |
"@types/react-dom": "^19.0.4",
|
| 54 |
"@types/react-speech-recognition": "^3.9.6",
|
|
|
|
| 57 |
"eslint-plugin-react-hooks": "^5.1.0",
|
| 58 |
"eslint-plugin-react-refresh": "^0.4.19",
|
| 59 |
"globals": "^15.15.0",
|
| 60 |
+
"tw-animate-css": "^1.3.0",
|
| 61 |
"typescript": "~5.7.2",
|
| 62 |
"typescript-eslint": "^8.24.1",
|
| 63 |
"vite": "^6.2.0"
|
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 { PROVIDERS } from "./utils/providers.js";
|
| 18 |
import { COLORS } from "./utils/colors.js";
|
| 19 |
|
| 20 |
// Load environment variables from .env file
|
|
@@ -30,7 +30,6 @@ 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 MODEL_ID = "deepseek-ai/DeepSeek-V3-0324";
|
| 34 |
const MAX_REQUESTS_PER_IP = 2;
|
| 35 |
|
| 36 |
app.use(cookieParser());
|
|
@@ -213,7 +212,7 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 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,
|
|
@@ -221,6 +220,30 @@ app.post("/api/ask-ai", async (req, res) => {
|
|
| 221 |
});
|
| 222 |
}
|
| 223 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
let { hf_token } = req.cookies;
|
| 225 |
let token = hf_token;
|
| 226 |
|
|
@@ -276,7 +299,7 @@ app.post("/api/ask-ai", async (req, res) => {
|
|
| 276 |
|
| 277 |
try {
|
| 278 |
const chatCompletion = client.chatCompletionStream({
|
| 279 |
-
model:
|
| 280 |
provider: selectedProvider.id,
|
| 281 |
messages: [
|
| 282 |
{
|
|
@@ -318,15 +341,8 @@ app.post("/api/ask-ai", async (req, res) => {
|
|
| 318 |
}
|
| 319 |
const chunk = value.choices[0]?.delta?.content;
|
| 320 |
if (chunk) {
|
| 321 |
-
|
| 322 |
-
|
| 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>");
|
|
@@ -336,6 +352,19 @@ app.post("/api/ask-ai", async (req, res) => {
|
|
| 336 |
if (newChunk.includes("</html>")) {
|
| 337 |
break;
|
| 338 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
}
|
| 340 |
}
|
| 341 |
}
|
|
|
|
| 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 |
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 |
app.use(cookieParser());
|
|
|
|
| 212 |
});
|
| 213 |
|
| 214 |
app.post("/api/ask-ai", async (req, res) => {
|
| 215 |
+
const { prompt, html, previousPrompt, provider, model } = req.body;
|
| 216 |
if (!prompt) {
|
| 217 |
return res.status(400).send({
|
| 218 |
ok: false,
|
|
|
|
| 220 |
});
|
| 221 |
}
|
| 222 |
|
| 223 |
+
if (!model) {
|
| 224 |
+
return res.status(400).send({
|
| 225 |
+
ok: false,
|
| 226 |
+
message: "Missing model field",
|
| 227 |
+
});
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
const selectedModel = MODELS.find(
|
| 231 |
+
(m) => m.value === model || m.label === model
|
| 232 |
+
);
|
| 233 |
+
if (!selectedModel) {
|
| 234 |
+
return res.status(400).send({
|
| 235 |
+
ok: false,
|
| 236 |
+
message: "Invalid model selected",
|
| 237 |
+
});
|
| 238 |
+
}
|
| 239 |
+
if (!selectedModel.providers.includes(provider) && provider !== "auto") {
|
| 240 |
+
return res.status(400).send({
|
| 241 |
+
ok: false,
|
| 242 |
+
openSelectProvider: true,
|
| 243 |
+
message: `The selected model does not support the ${provider} provider.`,
|
| 244 |
+
});
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
let { hf_token } = req.cookies;
|
| 248 |
let token = hf_token;
|
| 249 |
|
|
|
|
| 299 |
|
| 300 |
try {
|
| 301 |
const chatCompletion = client.chatCompletionStream({
|
| 302 |
+
model: selectedModel.value,
|
| 303 |
provider: selectedProvider.id,
|
| 304 |
messages: [
|
| 305 |
{
|
|
|
|
| 341 |
}
|
| 342 |
const chunk = value.choices[0]?.delta?.content;
|
| 343 |
if (chunk) {
|
| 344 |
+
let newChunk = chunk;
|
| 345 |
+
if (!selectedModel?.isThinker) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
if (chunk.includes("</html>")) {
|
| 347 |
// Replace everything after the last </html> tag with an empty string
|
| 348 |
newChunk = newChunk.replace(/<\/html>[\s\S]*/, "</html>");
|
|
|
|
| 352 |
if (newChunk.includes("</html>")) {
|
| 353 |
break;
|
| 354 |
}
|
| 355 |
+
} else {
|
| 356 |
+
// check if in the completeResponse there is already a </html> tag but after the last </think> tag, if yes break the loop
|
| 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 |
}
|
| 370 |
}
|
src/assets/index.css
CHANGED
|
@@ -1,4 +1,7 @@
|
|
| 1 |
@import "tailwindcss";
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
* {
|
| 4 |
font-family: "Noto Sans";
|
|
@@ -7,3 +10,129 @@
|
|
| 7 |
.font-code {
|
| 8 |
font-family: "Source Code Pro";
|
| 9 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
@import "tailwindcss";
|
| 2 |
+
@import "tw-animate-css";
|
| 3 |
+
|
| 4 |
+
@custom-variant dark (&:is(.dark *));
|
| 5 |
|
| 6 |
* {
|
| 7 |
font-family: "Noto Sans";
|
|
|
|
| 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 |
+
}
|
src/components/ask-ai/ask-ai.tsx
CHANGED
|
@@ -1,19 +1,20 @@
|
|
| 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 "
|
| 7 |
-
import {
|
| 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 "
|
| 13 |
import SuccessSound from "./../../assets/success.mp3";
|
| 14 |
import Settings from "../settings/settings";
|
| 15 |
import ProModal from "../pro-modal/pro-modal";
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
function AskAI({
|
| 19 |
html,
|
|
@@ -21,7 +22,6 @@ function AskAI({
|
|
| 21 |
onScrollToBottom,
|
| 22 |
isAiWorking,
|
| 23 |
setisAiWorking,
|
| 24 |
-
setView,
|
| 25 |
onNewPrompt,
|
| 26 |
onSuccess,
|
| 27 |
}: {
|
|
@@ -30,20 +30,23 @@ function AskAI({
|
|
| 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 |
-
|
| 46 |
-
const [
|
|
|
|
| 47 |
|
| 48 |
const audio = new Audio(SuccessSound);
|
| 49 |
audio.volume = 0.5;
|
|
@@ -62,6 +65,7 @@ function AskAI({
|
|
| 62 |
body: JSON.stringify({
|
| 63 |
prompt,
|
| 64 |
provider,
|
|
|
|
| 65 |
...(html === defaultHTML ? {} : { html }),
|
| 66 |
...(previousPrompt ? { previousPrompt } : {}),
|
| 67 |
}),
|
|
@@ -87,7 +91,9 @@ function AskAI({
|
|
| 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) {
|
|
@@ -97,7 +103,6 @@ function AskAI({
|
|
| 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(
|
|
@@ -113,9 +118,19 @@ function AskAI({
|
|
| 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 |
-
|
| 119 |
let partialDoc = newHtml;
|
| 120 |
if (!partialDoc.includes("</html>")) {
|
| 121 |
partialDoc += "\n</html>";
|
|
@@ -137,8 +152,6 @@ function AskAI({
|
|
| 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,38 +161,59 @@ function AskAI({
|
|
| 148 |
}
|
| 149 |
};
|
| 150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
return (
|
| 152 |
-
<div
|
| 153 |
-
|
| 154 |
-
|
| 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 |
-
<
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
)}
|
| 177 |
-
<div
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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-
|
| 183 |
placeholder={
|
| 184 |
hasAsked ? "What do you want to ask AI next?" : "Ask AI anything..."
|
| 185 |
}
|
|
@@ -195,18 +229,21 @@ function AskAI({
|
|
| 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 |
-
<
|
| 204 |
-
|
| 205 |
-
|
|
|
|
| 206 |
onClick={callAi}
|
| 207 |
>
|
| 208 |
<GrSend className="-translate-x-[1px]" />
|
| 209 |
-
</
|
| 210 |
</div>
|
| 211 |
</div>
|
| 212 |
<div
|
|
|
|
| 1 |
/* eslint-disable @typescript-eslint/no-explicit-any */
|
| 2 |
+
import { useState, useRef } 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 "sonner";
|
| 7 |
+
import { useLocalStorage, useUpdateEffect } from "react-use";
|
|
|
|
|
|
|
| 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 { X } from "lucide-react";
|
| 18 |
|
| 19 |
function AskAI({
|
| 20 |
html,
|
|
|
|
| 22 |
onScrollToBottom,
|
| 23 |
isAiWorking,
|
| 24 |
setisAiWorking,
|
|
|
|
| 25 |
onNewPrompt,
|
| 26 |
onSuccess,
|
| 27 |
}: {
|
|
|
|
| 30 |
onScrollToBottom: () => void;
|
| 31 |
isAiWorking: boolean;
|
| 32 |
onNewPrompt: (prompt: string) => void;
|
|
|
|
| 33 |
setisAiWorking: React.Dispatch<React.SetStateAction<boolean>>;
|
| 34 |
onSuccess: (h: string, p: string) => void;
|
| 35 |
}) {
|
| 36 |
+
const refThink = useRef<HTMLDivElement | null>(null);
|
| 37 |
+
|
| 38 |
const [open, setOpen] = useState(false);
|
| 39 |
const [prompt, setPrompt] = useState("");
|
| 40 |
const [hasAsked, setHasAsked] = useState(false);
|
| 41 |
const [previousPrompt, setPreviousPrompt] = useState("");
|
| 42 |
const [provider, setProvider] = useLocalStorage("provider", "auto");
|
| 43 |
+
const [model, setModel] = useLocalStorage("model", MODELS[0].value);
|
| 44 |
const [openProvider, setOpenProvider] = useState(false);
|
| 45 |
const [providerError, setProviderError] = useState("");
|
| 46 |
const [openProModal, setOpenProModal] = useState(false);
|
| 47 |
+
const [think, setThink] = useState<string | undefined>(undefined);
|
| 48 |
+
const [openThink, setOpenThink] = useState(false);
|
| 49 |
+
const [isThinking, setIsThinking] = useState(true);
|
| 50 |
|
| 51 |
const audio = new Audio(SuccessSound);
|
| 52 |
audio.volume = 0.5;
|
|
|
|
| 65 |
body: JSON.stringify({
|
| 66 |
prompt,
|
| 67 |
provider,
|
| 68 |
+
model,
|
| 69 |
...(html === defaultHTML ? {} : { html }),
|
| 70 |
...(previousPrompt ? { previousPrompt } : {}),
|
| 71 |
}),
|
|
|
|
| 91 |
}
|
| 92 |
const reader = request.body.getReader();
|
| 93 |
const decoder = new TextDecoder("utf-8");
|
| 94 |
+
const selectedModel = MODELS.find(
|
| 95 |
+
(m: { value: string }) => m.value === model
|
| 96 |
+
);
|
| 97 |
const read = async () => {
|
| 98 |
const { done, value } = await reader.read();
|
| 99 |
if (done) {
|
|
|
|
| 103 |
setisAiWorking(false);
|
| 104 |
setHasAsked(true);
|
| 105 |
audio.play();
|
|
|
|
| 106 |
|
| 107 |
// Now we have the complete HTML including </html>, so set it to be sure
|
| 108 |
const finalDoc = contentResponse.match(
|
|
|
|
| 118 |
|
| 119 |
const chunk = decoder.decode(value, { stream: true });
|
| 120 |
contentResponse += chunk;
|
| 121 |
+
if (selectedModel?.isThinker) {
|
| 122 |
+
const thinkMatch = contentResponse.match(/<think>[\s\S]*/)?.[0];
|
| 123 |
+
if (thinkMatch && !contentResponse?.includes("</think>")) {
|
| 124 |
+
if (!openThink && (think?.length ?? 0) < 3) {
|
| 125 |
+
setOpenThink(true);
|
| 126 |
+
}
|
| 127 |
+
setThink(thinkMatch.replace("<think>", "").trim());
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
const newHtml = contentResponse.match(/<!DOCTYPE html>[\s\S]*/)?.[0];
|
| 132 |
if (newHtml) {
|
| 133 |
+
setIsThinking(false);
|
| 134 |
let partialDoc = newHtml;
|
| 135 |
if (!partialDoc.includes("</html>")) {
|
| 136 |
partialDoc += "\n</html>";
|
|
|
|
| 152 |
|
| 153 |
read();
|
| 154 |
}
|
|
|
|
|
|
|
| 155 |
} catch (error: any) {
|
| 156 |
setisAiWorking(false);
|
| 157 |
toast.error(error.message);
|
|
|
|
| 161 |
}
|
| 162 |
};
|
| 163 |
|
| 164 |
+
useUpdateEffect(() => {
|
| 165 |
+
if (refThink.current) {
|
| 166 |
+
refThink.current.scrollTop = refThink.current.scrollHeight;
|
| 167 |
+
}
|
| 168 |
+
}, [think]);
|
| 169 |
+
|
| 170 |
return (
|
| 171 |
+
<div className="bg-neutral-800 border border-neutral-700 rounded-lg ring-[5px] focus-within:ring-sky-500/50 ring-transparent z-10 absolute bottom-3 left-3 w-[calc(100%-20px)] group">
|
| 172 |
+
{think && (
|
| 173 |
+
<div
|
| 174 |
+
ref={refThink}
|
| 175 |
+
className="w-full px-5 pt-4 border-b border-neutral-700 max-h-[300px] overflow-y-auto relative"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
>
|
| 177 |
+
<div className="sticky top-0 z-1 flex items-center justify-end">
|
| 178 |
+
<Button
|
| 179 |
+
size="xs"
|
| 180 |
+
onClick={() => {
|
| 181 |
+
setThink("");
|
| 182 |
+
setOpenThink(false);
|
| 183 |
+
}}
|
| 184 |
+
>
|
| 185 |
+
<X />
|
| 186 |
+
Close
|
| 187 |
+
</Button>
|
| 188 |
+
</div>
|
| 189 |
+
<div className="-translate-y-5">
|
| 190 |
+
<p className="text-xs text-neutral-400">AI is thinking...</p>
|
| 191 |
+
<p className="text-[13px] text-neutral-300 mt-1 whitespace-pre-line">
|
| 192 |
+
{think}
|
| 193 |
+
</p>
|
| 194 |
+
</div>
|
| 195 |
+
</div>
|
| 196 |
)}
|
| 197 |
+
<div
|
| 198 |
+
className={classNames(
|
| 199 |
+
"w-full relative flex items-center justify-between pl-3.5 p-2 lg:p-2 lg:pl-4",
|
| 200 |
+
{
|
| 201 |
+
"animate-pulse": isAiWorking,
|
| 202 |
+
}
|
| 203 |
+
)}
|
| 204 |
+
>
|
| 205 |
+
{isAiWorking && (
|
| 206 |
+
<div className="absolute bg-neutral-800 rounded-lg bottom-0 left-10 w-[calc(100%-92px)] h-full z-1 flex items-center justify-start max-lg:text-sm">
|
| 207 |
+
<p className="text-neutral-400 font-code">
|
| 208 |
+
AI is {isThinking ? "thinking" : "coding"}...
|
| 209 |
+
</p>
|
| 210 |
+
</div>
|
| 211 |
+
)}
|
| 212 |
+
<RiSparkling2Fill className="size-5 text-neutral-400 group-focus-within:text-sky-500" />
|
| 213 |
<input
|
| 214 |
type="text"
|
| 215 |
disabled={isAiWorking}
|
| 216 |
+
className="w-full bg-transparent max-lg:text-sm outline-none px-3 text-white placeholder:text-neutral-400 font-code"
|
| 217 |
placeholder={
|
| 218 |
hasAsked ? "What do you want to ask AI next?" : "Ask AI anything..."
|
| 219 |
}
|
|
|
|
| 229 |
{/* <SpeechPrompt setPrompt={setPrompt} /> */}
|
| 230 |
<Settings
|
| 231 |
provider={provider as string}
|
| 232 |
+
model={model as string}
|
| 233 |
onChange={setProvider}
|
| 234 |
+
onModelChange={setModel}
|
| 235 |
open={openProvider}
|
| 236 |
error={providerError}
|
| 237 |
onClose={setOpenProvider}
|
| 238 |
/>
|
| 239 |
+
<Button
|
| 240 |
+
variant="pink"
|
| 241 |
+
size="icon"
|
| 242 |
+
disabled={isAiWorking || !prompt.trim()}
|
| 243 |
onClick={callAi}
|
| 244 |
>
|
| 245 |
<GrSend className="-translate-x-[1px]" />
|
| 246 |
+
</Button>
|
| 247 |
</div>
|
| 248 |
</div>
|
| 249 |
<div
|
src/components/deploy-button/deploy-button.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
| 1 |
/* eslint-disable @typescript-eslint/no-explicit-any */
|
| 2 |
import { useState } from "react";
|
| 3 |
-
import
|
| 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">
|
|
@@ -26,18 +26,15 @@ const MsgToast = ({ url }: { url: string }) => (
|
|
| 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 |
|
|
@@ -66,10 +63,7 @@ function DeployButton({
|
|
| 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 {
|
|
@@ -79,7 +73,6 @@ function DeployButton({
|
|
| 79 |
toast.error(err.message);
|
| 80 |
} finally {
|
| 81 |
setLoading(false);
|
| 82 |
-
setOpen(false);
|
| 83 |
}
|
| 84 |
};
|
| 85 |
|
|
@@ -87,135 +80,85 @@ function DeployButton({
|
|
| 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 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
<
|
| 94 |
-
|
| 95 |
-
</
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 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 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
<
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
</p>
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
</div>
|
| 220 |
</div>
|
| 221 |
);
|
|
|
|
| 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 |
const MsgToast = ({ url }: { url: string }) => (
|
| 14 |
<div className="w-full flex items-center justify-center gap-3">
|
|
|
|
| 26 |
|
| 27 |
function DeployButton({
|
| 28 |
html,
|
|
|
|
| 29 |
auth,
|
| 30 |
setHtml,
|
| 31 |
prompts,
|
| 32 |
}: {
|
| 33 |
html: string;
|
|
|
|
| 34 |
auth?: Auth;
|
| 35 |
setHtml: (html: string) => void;
|
| 36 |
prompts: string[];
|
| 37 |
}) {
|
|
|
|
| 38 |
const [loading, setLoading] = useState(false);
|
| 39 |
const [path, setPath] = useState<string | undefined>(undefined);
|
| 40 |
|
|
|
|
| 63 |
toast.success(
|
| 64 |
<MsgToast
|
| 65 |
url={`https://huggingface.co/spaces/${response.path ?? path}`}
|
| 66 |
+
/>
|
|
|
|
|
|
|
|
|
|
| 67 |
);
|
| 68 |
setPath(response.path);
|
| 69 |
} else {
|
|
|
|
| 73 |
toast.error(err.message);
|
| 74 |
} finally {
|
| 75 |
setLoading(false);
|
|
|
|
| 76 |
}
|
| 77 |
};
|
| 78 |
|
|
|
|
| 80 |
<div className="flex items-center justify-end gap-5">
|
| 81 |
<LoadButton auth={auth} setHtml={setHtml} setPath={setPath} />
|
| 82 |
<div className="relative flex items-center justify-end">
|
| 83 |
+
<Popover>
|
| 84 |
+
<PopoverTrigger asChild>
|
| 85 |
+
<div>
|
| 86 |
+
<Button variant="pink" className="max-lg:hidden">
|
| 87 |
+
{path ? "Update Space" : "Deploy to Space"}
|
| 88 |
+
</Button>
|
| 89 |
+
<Button variant="pink" size="sm" className="lg:hidden">
|
| 90 |
+
{path ? "Update Space" : "Deploy to Space"}
|
| 91 |
+
</Button>
|
| 92 |
+
</div>
|
| 93 |
+
</PopoverTrigger>
|
| 94 |
+
<PopoverContent
|
| 95 |
+
className="p-0 overflow-hidden !bg-neutral-900"
|
| 96 |
+
align="end"
|
| 97 |
+
>
|
| 98 |
+
{!auth ? (
|
| 99 |
+
<Login html={html}>
|
| 100 |
+
<p className="text-muted-foreground text-sm mb-3">
|
| 101 |
+
Host this project for free and share it with your friends.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
</p>
|
| 103 |
+
</Login>
|
| 104 |
+
) : (
|
| 105 |
+
<>
|
| 106 |
+
<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">
|
| 107 |
+
<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">
|
| 108 |
+
<img src={SpaceIcon} alt="Space Icon" className="size-4" />
|
| 109 |
+
Space
|
| 110 |
+
</span>
|
| 111 |
+
Configure Deployment
|
| 112 |
+
</header>
|
| 113 |
+
<main className="px-4 pt-3 pb-4 space-y-3">
|
| 114 |
+
<p className="text-xs text-amber-600 bg-amber-500/10 rounded-md p-2">
|
| 115 |
+
{path ? (
|
| 116 |
+
<span>
|
| 117 |
+
Your space is live at{" "}
|
| 118 |
+
<a
|
| 119 |
+
href={`https://huggingface.co/spaces/${path}`}
|
| 120 |
+
target="_blank"
|
| 121 |
+
className="underline hover:text-amber-700"
|
| 122 |
+
>
|
| 123 |
+
huggingface.co/{path}
|
| 124 |
+
</a>
|
| 125 |
+
. You can update it by deploying again.
|
| 126 |
+
</span>
|
| 127 |
+
) : (
|
| 128 |
+
"Deploy your project to a space on the Hub. Spaces are a way to share your project with the world."
|
| 129 |
+
)}
|
| 130 |
</p>
|
| 131 |
+
{!path && (
|
| 132 |
+
<label className="block">
|
| 133 |
+
<p className="text-gray-600 text-sm font-medium mb-1.5">
|
| 134 |
+
Space Title
|
| 135 |
+
</p>
|
| 136 |
+
<input
|
| 137 |
+
type="text"
|
| 138 |
+
value={config.title}
|
| 139 |
+
className="mr-2 border rounded-md px-3 py-1.5 border-gray-300 w-full text-sm"
|
| 140 |
+
placeholder="My Awesome Space"
|
| 141 |
+
onChange={(e) =>
|
| 142 |
+
setConfig({ ...config, title: e.target.value })
|
| 143 |
+
}
|
| 144 |
+
/>
|
| 145 |
+
</label>
|
| 146 |
+
)}
|
| 147 |
+
<div className="pt-2 text-right">
|
| 148 |
+
<button
|
| 149 |
+
disabled={loading || (!path && !config.title)}
|
| 150 |
+
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"
|
| 151 |
+
onClick={createSpace}
|
| 152 |
+
>
|
| 153 |
+
{path ? "Update Space" : "Create Space"}
|
| 154 |
+
{loading && <Loading />}
|
| 155 |
+
</button>
|
| 156 |
+
</div>
|
| 157 |
+
</main>
|
| 158 |
+
</>
|
| 159 |
+
)}
|
| 160 |
+
</PopoverContent>
|
| 161 |
+
</Popover>
|
| 162 |
</div>
|
| 163 |
</div>
|
| 164 |
);
|
src/components/footer/footer.tsx
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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-7">
|
| 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,39 +1,66 @@
|
|
| 1 |
import { ReactNode } from "react";
|
| 2 |
-
import {
|
| 3 |
|
| 4 |
import Logo from "@/assets/logo.svg";
|
| 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
function Header({
|
| 7 |
-
|
|
|
|
| 8 |
children,
|
| 9 |
}: {
|
| 10 |
-
|
|
|
|
| 11 |
children?: ReactNode;
|
| 12 |
}) {
|
| 13 |
return (
|
| 14 |
-
<header className="border-b border-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
</header>
|
| 38 |
);
|
| 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 |
DeepSite
|
| 41 |
+
<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">
|
| 42 |
+
{" "}
|
| 43 |
+
v2
|
| 44 |
+
</span>
|
| 45 |
</h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
</div>
|
| 47 |
+
<div className="flex items-center justify-center gap-1">
|
| 48 |
+
{TABS.map((item) => (
|
| 49 |
+
<Button
|
| 50 |
+
key={item.value}
|
| 51 |
+
variant={tab === item.value ? "secondary" : "ghost"}
|
| 52 |
+
className={classNames("", {
|
| 53 |
+
"opacity-60": tab !== item.value,
|
| 54 |
+
})}
|
| 55 |
+
size="sm"
|
| 56 |
+
onClick={() => onNewTab(item.value)}
|
| 57 |
+
>
|
| 58 |
+
<item.icon className="size-4" />
|
| 59 |
+
<span className="hidden md:inline">{item.label}</span>
|
| 60 |
+
</Button>
|
| 61 |
+
))}
|
| 62 |
+
</div>
|
| 63 |
+
<div className="flex items-center justify-end gap-3">{children}</div>
|
| 64 |
</header>
|
| 65 |
);
|
| 66 |
}
|
src/components/history/history.tsx
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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} versions
|
| 17 |
+
</Button>
|
| 18 |
+
</PopoverTrigger>
|
| 19 |
+
<PopoverContent
|
| 20 |
+
className="!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 |
+
Version 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/load-button/load-button.tsx
CHANGED
|
@@ -1,10 +1,13 @@
|
|
| 1 |
import classNames from "classnames";
|
| 2 |
import { useState } from "react";
|
| 3 |
-
import { toast } from "
|
| 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,7 +20,6 @@ function LoadButton({
|
|
| 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,12 +40,10 @@ function LoadButton({
|
|
| 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,81 +54,73 @@ function LoadButton({
|
|
| 54 |
"border-r border-gray-700 pr-5": auth,
|
| 55 |
})}
|
| 56 |
>
|
| 57 |
-
<
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 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-
|
| 90 |
Load an existing DeepSite Space to continue working on it.
|
| 91 |
</p>
|
| 92 |
<label className="block">
|
| 93 |
-
<p className="text-
|
| 94 |
-
|
| 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 |
-
|
| 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 |
-
<
|
| 121 |
-
|
| 122 |
-
|
|
|
|
| 123 |
onClick={loadSpace}
|
| 124 |
>
|
| 125 |
-
Load
|
| 126 |
{loading && <Loading />}
|
| 127 |
-
</
|
| 128 |
</div>
|
| 129 |
</main>
|
| 130 |
-
|
| 131 |
-
</
|
| 132 |
</div>
|
| 133 |
);
|
| 134 |
}
|
|
|
|
| 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 |
}) {
|
| 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 |
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 |
"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="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 |
}
|
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-
|
| 27 |
-
<span className="text-xs bg-red-500
|
| 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
|
| 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-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 |
/>
|
src/components/magicui/grid-pattern.tsx
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,133 +1,67 @@
|
|
| 1 |
import classNames from "classnames";
|
| 2 |
-
import { useRef, useState } from "react";
|
| 3 |
-
import { FaLaptopCode } from "react-icons/fa6";
|
| 4 |
-
import { FaMobileAlt } from "react-icons/fa";
|
| 5 |
|
| 6 |
-
import {
|
| 7 |
-
import {
|
| 8 |
-
import {
|
| 9 |
|
| 10 |
function Preview({
|
| 11 |
html,
|
| 12 |
isResizing,
|
| 13 |
isAiWorking,
|
| 14 |
-
setView,
|
| 15 |
ref,
|
|
|
|
|
|
|
|
|
|
| 16 |
}: {
|
| 17 |
html: string;
|
| 18 |
isResizing: boolean;
|
| 19 |
isAiWorking: boolean;
|
| 20 |
-
setView: React.Dispatch<React.SetStateAction<"editor" | "preview">>;
|
| 21 |
ref: React.RefObject<HTMLDivElement | null>;
|
|
|
|
|
|
|
|
|
|
| 22 |
}) {
|
| 23 |
-
const [device, setDevice] = useState<"desktop" | "mobile">("desktop");
|
| 24 |
-
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
| 25 |
-
|
| 26 |
-
const handleRefreshIframe = () => {
|
| 27 |
-
if (iframeRef.current) {
|
| 28 |
-
const iframe = iframeRef.current;
|
| 29 |
-
const content = iframe.srcdoc;
|
| 30 |
-
iframe.srcdoc = "";
|
| 31 |
-
setTimeout(() => {
|
| 32 |
-
iframe.srcdoc = content;
|
| 33 |
-
}, 10);
|
| 34 |
-
}
|
| 35 |
-
};
|
| 36 |
-
|
| 37 |
return (
|
| 38 |
<div
|
| 39 |
ref={ref}
|
| 40 |
className={classNames(
|
| 41 |
-
"w-full border-l border-gray-900
|
| 42 |
{
|
| 43 |
-
"
|
| 44 |
}
|
| 45 |
)}
|
| 46 |
onClick={(e) => {
|
| 47 |
if (isAiWorking) {
|
| 48 |
e.preventDefault();
|
| 49 |
e.stopPropagation();
|
| 50 |
-
toast.
|
| 51 |
}
|
| 52 |
}}
|
| 53 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
<iframe
|
| 55 |
ref={iframeRef}
|
| 56 |
title="output"
|
| 57 |
className={classNames(
|
| 58 |
-
"w-full select-none transition-all duration-200",
|
| 59 |
{
|
| 60 |
"pointer-events-none": isResizing || isAiWorking,
|
| 61 |
-
"max-w-md mx-auto h-[80dvh] rounded-[64px] border-[8px] border-
|
| 62 |
device === "mobile",
|
| 63 |
"h-full": device === "desktop",
|
|
|
|
|
|
|
| 64 |
}
|
| 65 |
)}
|
| 66 |
srcDoc={html}
|
| 67 |
/>
|
| 68 |
-
<div className="flex items-center justify-start gap-3 absolute bottom-3 lg:bottom-5 max-lg:left-3 lg:right-5">
|
| 69 |
-
<button
|
| 70 |
-
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"
|
| 71 |
-
onClick={() => setView("editor")}
|
| 72 |
-
>
|
| 73 |
-
<FaLaptopCode className="text-sm" />
|
| 74 |
-
Hide preview
|
| 75 |
-
</button>
|
| 76 |
-
{html === defaultHTML && (
|
| 77 |
-
<a
|
| 78 |
-
href="https://huggingface.co/spaces/victor/deepsite-gallery"
|
| 79 |
-
target="_blank"
|
| 80 |
-
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"
|
| 81 |
-
>
|
| 82 |
-
🖼️ <span>DeepSite Gallery</span>
|
| 83 |
-
</a>
|
| 84 |
-
)}
|
| 85 |
-
{html !== defaultHTML && !isAiWorking && (
|
| 86 |
-
<div className="flex items-center rounded-lg p-1 bg-gray-200 relative overflow-hidden z-0 max-lg:hidden">
|
| 87 |
-
<div
|
| 88 |
-
className={classNames(
|
| 89 |
-
"absolute left-1 top-1 rounded-md bg-black w-10 h-8 -z-[1] transition-all duration-200",
|
| 90 |
-
{
|
| 91 |
-
"translate-x-full": device === "mobile",
|
| 92 |
-
}
|
| 93 |
-
)}
|
| 94 |
-
/>
|
| 95 |
-
<button
|
| 96 |
-
className={classNames(
|
| 97 |
-
"rounded-md text-gray-500 w-10 h-8 flex items-center justify-center cursor-pointer",
|
| 98 |
-
{
|
| 99 |
-
"!text-white": device === "desktop",
|
| 100 |
-
"hover:bg-gray-300/60": device !== "desktop",
|
| 101 |
-
}
|
| 102 |
-
)}
|
| 103 |
-
onClick={() => setDevice("desktop")}
|
| 104 |
-
>
|
| 105 |
-
<FaLaptopCode className="text-sm" />
|
| 106 |
-
</button>
|
| 107 |
-
<button
|
| 108 |
-
className={classNames(
|
| 109 |
-
"rounded-md text-gray-500 w-10 h-8 flex items-center justify-center cursor-pointer",
|
| 110 |
-
{
|
| 111 |
-
"!text-white": device === "mobile",
|
| 112 |
-
"hover:bg-gray-300/60": device !== "mobile",
|
| 113 |
-
}
|
| 114 |
-
)}
|
| 115 |
-
onClick={() => setDevice("mobile")}
|
| 116 |
-
>
|
| 117 |
-
<FaMobileAlt className="text-sm" />
|
| 118 |
-
</button>
|
| 119 |
-
</div>
|
| 120 |
-
)}
|
| 121 |
-
{!isAiWorking && (
|
| 122 |
-
<button
|
| 123 |
-
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"
|
| 124 |
-
onClick={handleRefreshIframe}
|
| 125 |
-
>
|
| 126 |
-
<TbReload className="text-sm" />
|
| 127 |
-
Refresh Preview
|
| 128 |
-
</button>
|
| 129 |
-
)}
|
| 130 |
-
</div>
|
| 131 |
</div>
|
| 132 |
);
|
| 133 |
}
|
|
|
|
| 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 transition-all duration-200 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 |
ref={iframeRef}
|
| 51 |
title="output"
|
| 52 |
className={classNames(
|
| 53 |
+
"w-full select-none transition-all duration-200 bg-black max-lg:h-full",
|
| 54 |
{
|
| 55 |
"pointer-events-none": isResizing || isAiWorking,
|
| 56 |
+
"lg:max-w-md lg:mx-auto lg:h-[80dvh] lg:!rounded-[64px] lg:border-[8px] lg:border-neutral-700 lg:shadow-2xl":
|
| 57 |
device === "mobile",
|
| 58 |
"h-full": device === "desktop",
|
| 59 |
+
"lg:border-[8px] lg:border-neutral-700 lg:shadow-2xl lg:rounded-[44px]":
|
| 60 |
+
currentTab !== "preview" && device === "desktop",
|
| 61 |
}
|
| 62 |
)}
|
| 63 |
srcDoc={html}
|
| 64 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
</div>
|
| 66 |
);
|
| 67 |
}
|
src/components/settings/settings.tsx
CHANGED
|
@@ -1,136 +1,194 @@
|
|
| 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 |
-
<
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
<
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
"
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 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-
|
| 61 |
>
|
| 62 |
How to use it locally?
|
| 63 |
-
<
|
| 64 |
-
See the guide
|
| 65 |
-
</button>
|
| 66 |
</a>
|
| 67 |
-
|
| 68 |
-
<p className="text-
|
| 69 |
-
|
| 70 |
</p>
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
<div
|
| 109 |
-
key={id}
|
| 110 |
className={classNames(
|
| 111 |
-
"
|
| 112 |
{
|
| 113 |
-
"bg-
|
| 114 |
-
id === provider,
|
| 115 |
-
"hover:bg-gray-100 border-gray-100": id !== provider,
|
| 116 |
}
|
| 117 |
)}
|
| 118 |
onClick={() => {
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
}}
|
| 121 |
>
|
| 122 |
-
<
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
| 126 |
/>
|
| 127 |
-
{PROVIDERS[id].name}
|
| 128 |
</div>
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
</div>
|
| 131 |
-
</
|
| 132 |
-
</
|
| 133 |
-
</
|
| 134 |
</div>
|
| 135 |
);
|
| 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 (!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="icon">
|
| 60 |
+
<PiGearSixFill className="size-5" />
|
| 61 |
+
</Button>
|
| 62 |
+
</PopoverTrigger>
|
| 63 |
+
<PopoverContent
|
| 64 |
+
className="p-0 !w-96 overflow-hidden !bg-neutral-900"
|
| 65 |
+
align="center"
|
| 66 |
+
>
|
| 67 |
+
<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">
|
| 68 |
+
{/* <span className="text-xs bg-blue-500 text-white rounded-full px-1.5 py-0.5">
|
| 69 |
+
Provider
|
| 70 |
+
</span> */}
|
| 71 |
+
Customize Settings
|
| 72 |
+
</header>
|
| 73 |
+
<main className="px-4 pt-5 pb-6 space-y-5">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
<a
|
| 75 |
href="https://huggingface.co/spaces/enzostvs/deepsite/discussions/74"
|
| 76 |
target="_blank"
|
| 77 |
+
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"
|
| 78 |
>
|
| 79 |
How to use it locally?
|
| 80 |
+
<Button size="xs">See guide</Button>
|
|
|
|
|
|
|
| 81 |
</a>
|
| 82 |
+
{error !== "" && (
|
| 83 |
+
<p className="text-red-500 text-sm font-medium mb-2 flex items-center justify-between bg-red-500/10 p-2 rounded-md">
|
| 84 |
+
{error}
|
| 85 |
</p>
|
| 86 |
+
)}
|
| 87 |
+
<label className="block">
|
| 88 |
+
<p className="text-neutral-300 text-sm mb-2.5">
|
| 89 |
+
Choose a DeepSeek model
|
| 90 |
+
</p>
|
| 91 |
+
<Select defaultValue={model} onValueChange={onModelChange}>
|
| 92 |
+
<SelectTrigger className="w-full">
|
| 93 |
+
<SelectValue placeholder="Select a DeepSeek model" />
|
| 94 |
+
</SelectTrigger>
|
| 95 |
+
<SelectContent>
|
| 96 |
+
<SelectGroup>
|
| 97 |
+
<SelectLabel>DeepSeek models</SelectLabel>
|
| 98 |
+
{MODELS.map(
|
| 99 |
+
({
|
| 100 |
+
value,
|
| 101 |
+
label,
|
| 102 |
+
isNew = false,
|
| 103 |
+
}: {
|
| 104 |
+
value: string;
|
| 105 |
+
label: string;
|
| 106 |
+
isNew?: boolean;
|
| 107 |
+
}) => (
|
| 108 |
+
<SelectItem value={value} className="">
|
| 109 |
+
{label}
|
| 110 |
+
{isNew && (
|
| 111 |
+
<span className="text-xs bg-gradient-to-br from-sky-400 to-sky-600 text-white rounded-full px-1.5 py-0.5">
|
| 112 |
+
New
|
| 113 |
+
</span>
|
| 114 |
+
)}
|
| 115 |
+
</SelectItem>
|
| 116 |
+
)
|
| 117 |
+
)}
|
| 118 |
+
</SelectGroup>
|
| 119 |
+
</SelectContent>
|
| 120 |
+
</Select>
|
| 121 |
+
</label>
|
| 122 |
+
<div className="flex flex-col gap-3">
|
| 123 |
+
<div className="flex items-center justify-between">
|
| 124 |
+
<div>
|
| 125 |
+
<p className="text-neutral-300 text-sm mb-1.5">
|
| 126 |
+
Use auto-provider
|
| 127 |
+
</p>
|
| 128 |
+
<p className="text-xs text-neutral-400/70">
|
| 129 |
+
We'll automatically select the best provider for you based
|
| 130 |
+
on your prompt.
|
| 131 |
+
</p>
|
| 132 |
+
</div>
|
| 133 |
<div
|
|
|
|
| 134 |
className={classNames(
|
| 135 |
+
"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",
|
| 136 |
{
|
| 137 |
+
"!bg-sky-500": provider === "auto",
|
|
|
|
|
|
|
| 138 |
}
|
| 139 |
)}
|
| 140 |
onClick={() => {
|
| 141 |
+
const model = MODELS.find(
|
| 142 |
+
(m: { value: string }) => m.value === model
|
| 143 |
+
);
|
| 144 |
+
onChange(
|
| 145 |
+
provider === "auto"
|
| 146 |
+
? model?.autoProvider ?? "novita"
|
| 147 |
+
: "auto"
|
| 148 |
+
);
|
| 149 |
}}
|
| 150 |
>
|
| 151 |
+
<div
|
| 152 |
+
className={classNames(
|
| 153 |
+
"w-4 h-4 rounded-full shadow-md transition-all duration-200 bg-neutral-200",
|
| 154 |
+
{
|
| 155 |
+
"translate-x-4": provider === "auto",
|
| 156 |
+
}
|
| 157 |
+
)}
|
| 158 |
/>
|
|
|
|
| 159 |
</div>
|
| 160 |
+
</div>
|
| 161 |
+
<label className="block">
|
| 162 |
+
<p className="text-neutral-300 text-sm mb-2">
|
| 163 |
+
Inference Provider
|
| 164 |
+
</p>
|
| 165 |
+
<div className="grid grid-cols-2 gap-1.5">
|
| 166 |
+
{modelAvailableProviders.map((id: string) => (
|
| 167 |
+
<Button
|
| 168 |
+
key={id}
|
| 169 |
+
variant={id === provider ? "default" : "secondary"}
|
| 170 |
+
size="sm"
|
| 171 |
+
onClick={() => {
|
| 172 |
+
onChange(id);
|
| 173 |
+
}}
|
| 174 |
+
>
|
| 175 |
+
<img
|
| 176 |
+
src={`/providers/${id}.svg`}
|
| 177 |
+
alt={PROVIDERS[id].name}
|
| 178 |
+
className="size-5 mr-2"
|
| 179 |
+
/>
|
| 180 |
+
{PROVIDERS[id].name}
|
| 181 |
+
{id === provider && (
|
| 182 |
+
<RiCheckboxCircleFill className="ml-2 size-4 text-blue-500" />
|
| 183 |
+
)}
|
| 184 |
+
</Button>
|
| 185 |
+
))}
|
| 186 |
+
</div>
|
| 187 |
+
</label>
|
| 188 |
</div>
|
| 189 |
+
</main>
|
| 190 |
+
</PopoverContent>
|
| 191 |
+
</Popover>
|
| 192 |
</div>
|
| 193 |
);
|
| 194 |
}
|
src/components/tabs/tabs.tsx
DELETED
|
@@ -1,120 +0,0 @@
|
|
| 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
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|
| 15 |
+
"bg-red-500 text-white shadow-xs hover:bg-red-500/90 focus-visible:ring-red-500/20 dark:focus-visible:ring-red-500/40 dark:bg-red-500/60 dark:bg-red-900 dark:hover:bg-red-900/90 dark:focus-visible:ring-red-900/20 dark:dark:focus-visible:ring-red-900/40 dark:dark:bg-red-900/60",
|
| 16 |
+
outline:
|
| 17 |
+
"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",
|
| 18 |
+
secondary:
|
| 19 |
+
"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",
|
| 20 |
+
ghost:
|
| 21 |
+
"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",
|
| 22 |
+
link: "text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50",
|
| 23 |
+
pink: "bg-sky-500 text-white hover:brightness-110",
|
| 24 |
+
gray: "bg-neutral-900 text-neutral-300 hover:brightness-110",
|
| 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 |
+
xs: "h-6 text-xs rounded-full pl-2 pr-2 gap-1",
|
| 33 |
+
},
|
| 34 |
+
},
|
| 35 |
+
defaultVariants: {
|
| 36 |
+
variant: "default",
|
| 37 |
+
size: "default",
|
| 38 |
+
},
|
| 39 |
+
}
|
| 40 |
+
);
|
| 41 |
+
|
| 42 |
+
function Button({
|
| 43 |
+
className,
|
| 44 |
+
variant,
|
| 45 |
+
size,
|
| 46 |
+
asChild = false,
|
| 47 |
+
...props
|
| 48 |
+
}: React.ComponentProps<"button"> &
|
| 49 |
+
VariantProps<typeof buttonVariants> & {
|
| 50 |
+
asChild?: boolean;
|
| 51 |
+
}) {
|
| 52 |
+
const Comp = asChild ? Slot : "button";
|
| 53 |
+
|
| 54 |
+
return (
|
| 55 |
+
<Comp
|
| 56 |
+
data-slot="button"
|
| 57 |
+
className={cn(buttonVariants({ variant, size, className }))}
|
| 58 |
+
{...props}
|
| 59 |
+
/>
|
| 60 |
+
);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
export { Button, buttonVariants };
|
src/components/ui/dropdown-menu.tsx
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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/lib/utils.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,12 +1,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 "./
|
| 6 |
|
| 7 |
createRoot(document.getElementById("root")!).render(
|
| 8 |
<StrictMode>
|
| 9 |
<App />
|
| 10 |
-
<ToastContainer className="pt-11 max-md:p-4" />
|
| 11 |
</StrictMode>
|
| 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 |
);
|
src/{components → views}/App.tsx
RENAMED
|
@@ -1,45 +1,48 @@
|
|
| 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 |
-
|
| 7 |
-
useUnmount,
|
| 8 |
useEvent,
|
| 9 |
useLocalStorage,
|
|
|
|
| 10 |
useSearchParam,
|
|
|
|
|
|
|
| 11 |
} from "react-use";
|
| 12 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
-
import
|
| 15 |
-
import
|
| 16 |
-
import {
|
| 17 |
-
import
|
| 18 |
-
import
|
| 19 |
-
import
|
| 20 |
-
import
|
|
|
|
| 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 [
|
| 41 |
-
|
| 42 |
-
|
|
|
|
| 43 |
|
| 44 |
const fetchMe = async () => {
|
| 45 |
const res = await fetch("/api/@me");
|
|
@@ -127,36 +130,20 @@ function App() {
|
|
| 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.
|
| 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,118 +153,143 @@ function App() {
|
|
| 166 |
window.removeEventListener("resize", resetLayout);
|
| 167 |
});
|
| 168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
return (
|
| 170 |
-
<
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 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 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
/>
|
| 239 |
-
|
| 240 |
-
|
| 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 |
-
|
|
|
|
|
|
|
| 277 |
/>
|
| 278 |
</main>
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
);
|
| 281 |
}
|
| 282 |
-
|
| 283 |
-
export default App;
|
|
|
|
| 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 |
|
|
|
|
|
|
|
| 38 |
const [html, setHtml] = useState((htmlStorage as string) ?? defaultHTML);
|
| 39 |
const [isAiWorking, setisAiWorking] = useState(false);
|
| 40 |
const [auth, setAuth] = useState<Auth | undefined>(undefined);
|
|
|
|
|
|
|
|
|
|
| 41 |
const [prompts, setPrompts] = useState<string[]>([]);
|
| 42 |
+
const [device, setDevice] = useState<"desktop" | "mobile">("desktop");
|
| 43 |
+
const [htmlHistory, setHtmlHistory] = useState<HtmlHistory[]>([]);
|
| 44 |
+
const [currentTab, setCurrentTab] = useState("chat");
|
| 45 |
+
const [isResizing, setIsResizing] = useState(false);
|
| 46 |
|
| 47 |
const fetchMe = async () => {
|
| 48 |
const res = await fetch("/api/@me");
|
|
|
|
| 130 |
document.removeEventListener("mouseup", handleMouseUp);
|
| 131 |
};
|
| 132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
useMount(() => {
|
|
|
|
| 134 |
fetchMe();
|
| 135 |
fetchRemix();
|
| 136 |
|
|
|
|
| 137 |
if (htmlStorage) {
|
| 138 |
removeHtmlStorage();
|
| 139 |
+
toast.warning("Previous HTML content restored from local storage.");
|
| 140 |
}
|
| 141 |
|
|
|
|
| 142 |
resetLayout();
|
|
|
|
|
|
|
| 143 |
if (!resizer.current) return;
|
| 144 |
resizer.current.addEventListener("mousedown", handleMouseDown);
|
| 145 |
window.addEventListener("resize", resetLayout);
|
| 146 |
});
|
|
|
|
|
|
|
| 147 |
useUnmount(() => {
|
| 148 |
document.removeEventListener("mousemove", handleResize);
|
| 149 |
document.removeEventListener("mouseup", handleMouseUp);
|
|
|
|
| 153 |
window.removeEventListener("resize", resetLayout);
|
| 154 |
});
|
| 155 |
|
| 156 |
+
// Prevent accidental navigation away when AI is working or content has changed
|
| 157 |
+
useEvent("beforeunload", (e) => {
|
| 158 |
+
if (isAiWorking || html !== defaultHTML) {
|
| 159 |
+
e.preventDefault();
|
| 160 |
+
return "";
|
| 161 |
+
}
|
| 162 |
+
});
|
| 163 |
+
|
| 164 |
+
useUpdateEffect(() => {
|
| 165 |
+
if (currentTab === "chat") {
|
| 166 |
+
// Reset editor width when switching to reasoning tab
|
| 167 |
+
resetLayout();
|
| 168 |
+
} else {
|
| 169 |
+
if (preview.current) {
|
| 170 |
+
// Reset preview width when switching to preview tab
|
| 171 |
+
preview.current.style.width = "100%";
|
| 172 |
+
}
|
| 173 |
+
}
|
| 174 |
+
}, [currentTab]);
|
| 175 |
+
|
| 176 |
return (
|
| 177 |
+
<ThemeProvider
|
| 178 |
+
defaultTheme="dark"
|
| 179 |
+
storageKey="deepsite-ui-theme"
|
| 180 |
+
className="h-screen bg-slate-100 dark:bg-neutral-950 flex flex-col"
|
| 181 |
+
>
|
| 182 |
+
<Header tab={currentTab} onNewTab={setCurrentTab}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
<DeployButton
|
| 184 |
html={html}
|
|
|
|
| 185 |
auth={auth}
|
| 186 |
setHtml={setHtml}
|
| 187 |
prompts={prompts}
|
| 188 |
/>
|
| 189 |
</Header>
|
| 190 |
+
<main className="bg-neutral-950 flex-1 max-lg:flex-col flex w-full">
|
| 191 |
+
{currentTab === "chat" && (
|
| 192 |
+
<>
|
| 193 |
+
<div ref={editor} className="relative">
|
| 194 |
+
<CopyIcon
|
| 195 |
+
className="size-4 absolute top-2 right-5 text-neutral-500 hover:text-neutral-300 z-2 cursor-pointer"
|
| 196 |
+
onClick={() => {
|
| 197 |
+
copyToClipboard(html);
|
| 198 |
+
toast.success("HTML copied to clipboard!");
|
| 199 |
+
}}
|
| 200 |
+
/>
|
| 201 |
+
<Editor
|
| 202 |
+
language="html"
|
| 203 |
+
theme="vs-dark"
|
| 204 |
+
className={classNames(
|
| 205 |
+
"h-[calc(100dvh-98px)] lg:h-full bg-neutral-900 transition-all duration-200 ",
|
| 206 |
+
{
|
| 207 |
+
"pointer-events-none": isAiWorking,
|
| 208 |
+
}
|
| 209 |
+
)}
|
| 210 |
+
options={{
|
| 211 |
+
colorDecorators: true,
|
| 212 |
+
fontLigatures: true,
|
| 213 |
+
theme: "vs-dark",
|
| 214 |
+
minimap: { enabled: false },
|
| 215 |
+
}}
|
| 216 |
+
value={html}
|
| 217 |
+
onChange={(value) => {
|
| 218 |
+
const newValue = value ?? "";
|
| 219 |
+
setHtml(newValue);
|
| 220 |
+
}}
|
| 221 |
+
onMount={(editor) => (editorRef.current = editor)}
|
| 222 |
+
/>
|
| 223 |
+
<AskAI
|
| 224 |
+
html={html}
|
| 225 |
+
setHtml={(newHtml: string) => {
|
| 226 |
+
setHtml(newHtml);
|
| 227 |
+
}}
|
| 228 |
+
onSuccess={(finalHtml: string, p: string) => {
|
| 229 |
+
const currentHistory = [...htmlHistory];
|
| 230 |
+
currentHistory.unshift({
|
| 231 |
+
html: finalHtml,
|
| 232 |
+
createdAt: new Date(),
|
| 233 |
+
prompt: p,
|
| 234 |
+
});
|
| 235 |
+
setHtmlHistory(currentHistory);
|
| 236 |
+
// if xs or sm
|
| 237 |
+
if (window.innerWidth <= 1024) {
|
| 238 |
+
setCurrentTab("preview");
|
| 239 |
+
}
|
| 240 |
+
}}
|
| 241 |
+
isAiWorking={isAiWorking}
|
| 242 |
+
setisAiWorking={setisAiWorking}
|
| 243 |
+
onNewPrompt={(prompt: string) => {
|
| 244 |
+
setPrompts((prev) => [...prev, prompt]);
|
| 245 |
+
}}
|
| 246 |
+
onScrollToBottom={() => {
|
| 247 |
+
editorRef.current?.revealLine(
|
| 248 |
+
editorRef.current?.getModel()?.getLineCount() ?? 0
|
| 249 |
+
);
|
| 250 |
+
}}
|
| 251 |
+
/>
|
| 252 |
+
</div>
|
| 253 |
+
<div
|
| 254 |
+
ref={resizer}
|
| 255 |
+
className="bg-neutral-800 hover:bg-sky-500 active:bg-sky-500 w-1.5 cursor-col-resize h-full max-lg:hidden"
|
| 256 |
/>
|
| 257 |
+
</>
|
| 258 |
+
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
<Preview
|
| 260 |
html={html}
|
| 261 |
isResizing={isResizing}
|
| 262 |
isAiWorking={isAiWorking}
|
| 263 |
ref={preview}
|
| 264 |
+
device={device}
|
| 265 |
+
currentTab={currentTab}
|
| 266 |
+
iframeRef={iframeRef}
|
| 267 |
/>
|
| 268 |
</main>
|
| 269 |
+
<Footer
|
| 270 |
+
onReset={() => {
|
| 271 |
+
if (isAiWorking) {
|
| 272 |
+
toast.warning("Please wait for the AI to finish working.");
|
| 273 |
+
return;
|
| 274 |
+
}
|
| 275 |
+
if (
|
| 276 |
+
window.confirm("You're about to reset the editor. Are you sure?")
|
| 277 |
+
) {
|
| 278 |
+
setHtml(defaultHTML);
|
| 279 |
+
removeHtmlStorage();
|
| 280 |
+
editorRef.current?.revealLine(
|
| 281 |
+
editorRef.current?.getModel()?.getLineCount() ?? 0
|
| 282 |
+
);
|
| 283 |
+
}
|
| 284 |
+
}}
|
| 285 |
+
htmlHistory={htmlHistory}
|
| 286 |
+
setHtml={setHtml}
|
| 287 |
+
iframeRef={iframeRef}
|
| 288 |
+
auth={auth}
|
| 289 |
+
device={device}
|
| 290 |
+
setDevice={setDevice}
|
| 291 |
+
/>
|
| 292 |
+
<Toaster richColors position="bottom-center" />
|
| 293 |
+
</ThemeProvider>
|
| 294 |
);
|
| 295 |
}
|
|
|
|
|
|
tsconfig.app.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
| 6 |
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
| 7 |
"module": "ESNext",
|
| 8 |
"skipLibCheck": true,
|
|
|
|
| 9 |
|
| 10 |
/* Bundler mode */
|
| 11 |
"moduleResolution": "bundler",
|
|
|
|
| 6 |
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
| 7 |
"module": "ESNext",
|
| 8 |
"skipLibCheck": true,
|
| 9 |
+
"composite": true,
|
| 10 |
|
| 11 |
/* Bundler mode */
|
| 12 |
"moduleResolution": "bundler",
|
tsconfig.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
| 1 |
{
|
| 2 |
"files": [],
|
| 3 |
"references": [
|
| 4 |
-
{
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
],
|
| 7 |
"compilerOptions": {
|
| 8 |
"baseUrl": ".",
|
| 9 |
"paths": {
|
| 10 |
-
"@/*": ["src/*"]
|
| 11 |
}
|
| 12 |
}
|
| 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 |
}
|
utils/providers.js
CHANGED
|
@@ -19,9 +19,26 @@ export const PROVIDERS = {
|
|
| 19 |
max_tokens: 16_000,
|
| 20 |
id: "novita",
|
| 21 |
},
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
max_tokens: 16_000,
|
| 20 |
id: "novita",
|
| 21 |
},
|
| 22 |
+
hyperbolic: {
|
| 23 |
+
name: "Hyperbolic",
|
| 24 |
+
max_tokens: 131_000,
|
| 25 |
+
id: "hyperbolic",
|
| 26 |
+
},
|
| 27 |
};
|
| 28 |
+
|
| 29 |
+
export const MODELS = [
|
| 30 |
+
{
|
| 31 |
+
value: "deepseek-ai/DeepSeek-V3-0324",
|
| 32 |
+
label: "DeepSeek V3 O324",
|
| 33 |
+
providers: ["fireworks-ai", "nebius", "sambanova", "novita", "hyperbolic"],
|
| 34 |
+
autoProvider: "sambanova",
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
value: "deepseek-ai/DeepSeek-R1-0528",
|
| 38 |
+
label: "DeepSeek R1 0528",
|
| 39 |
+
providers: ["fireworks-ai", "novita", "hyperbolic", "nebius"],
|
| 40 |
+
autoProvider: "novita",
|
| 41 |
+
isNew: true,
|
| 42 |
+
isThinker: true,
|
| 43 |
+
},
|
| 44 |
+
];
|
utils/types.ts
CHANGED
|
@@ -4,3 +4,9 @@ export interface Auth {
|
|
| 4 |
name: string;
|
| 5 |
isLocalUse?: boolean;
|
| 6 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
name: string;
|
| 5 |
isLocalUse?: boolean;
|
| 6 |
}
|
| 7 |
+
|
| 8 |
+
export interface HtmlHistory {
|
| 9 |
+
html: string;
|
| 10 |
+
createdAt: Date;
|
| 11 |
+
prompt: string;
|
| 12 |
+
}
|
vite.config.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
| 1 |
-
import
|
| 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:
|
|
|
|
|
|
|
| 10 |
},
|
| 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 |
});
|