diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14f5d94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Dependencies +node_modules/ +.pnp +.pnp.js + +# Testing +coverage/ + +# Next.js +.next/ +out/ + +# Production +build/ + +# Misc +.DS_Store +*.pem + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Local env files +.env*.local +.env + +# Vercel +.vercel + +# TypeScript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/README.md b/README.md index 373578b..05e5442 100644 --- a/README.md +++ b/README.md @@ -1 +1,152 @@ -# upload-GLTF \ No newline at end of file +# upload-GLTF + +A secure upload interface for 3D assets with automatic deployment to GitHub via Git LFS. + +## Description + +This is a Next.js web application that allows designers and developers to upload 3D models and textures via a modern drag-and-drop interface. Uploaded files are automatically committed and pushed to a GitHub repository using Git LFS for efficient version control of large binary files. + +## Features + +- **Drag-and-drop upload** for 3D models (.glb, .gltf, .fbx) and textures (.png, .jpg, .jpeg, .webp, .ktx2) +- **2 GB max** file size per upload +- **Automatic Git deployment** — files are committed and pushed to GitHub via Git LFS +- **Secret key authentication** for secure uploads +- **Modern UI** built with Tailwind CSS and React + +## Prerequisites + +- Node.js 18+ +- npm or yarn + +## Installation + +```bash +git clone +cd upload-GLTF +npm install +``` + +## Configuration + +### Environment Variables + +Create a `.env.local` file in the root directory: + +```env +UPLOAD_SECRET_KEY=your-secret-key-here +GIT_BRANCH=main +GIT_REPO_URL=git@github.com:username/repo.git +``` + +| Variable | Description | Required | +|----------|-------------|----------| +| `UPLOAD_SECRET_KEY` | Secret key for authentication | Yes | +| `GIT_BRANCH` | Git branch to push to (default: main) | No | +| `GIT_REPO_URL` | Git repository URL for SSH push | No | + +### Git LFS Setup + +Install Git LFS and track the asset file types by creating a `.gitattributes` file: + +```bash +git lfs install +``` + +Create `.gitattributes`: + +``` +*.glb filter=lfs diff=lfs merge=lfs -text +*.gltf filter=lfs diff=lfs merge=lfs -text +*.fbx filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text +*.ktx2 filter=lfs diff=lfs merge=lfs -text +``` + +## Usage + +### Development + +```bash +npm run dev +``` + +Access the app at `http://localhost:3000` + +### Production + +```bash +npm run build +npm start +``` + +### Lint + +```bash +npm run lint +``` + +## Docker Deployment + +### Docker Compose + +Create `docker-compose.yml`: + +```yaml +services: + app: + build: . + ports: + - "3000:3000" + environment: + - UPLOAD_SECRET_KEY=your-secret-key + - GIT_BRANCH=main + - GIT_REPO_URL=git@github.com:username/repo.git + volumes: + - ./public:/app/public + - ~/.ssh:/root/.ssh +``` + +Build and run: + +```bash +docker-compose up --build +``` + +### Coolify + +The Dockerfile is optimized for Coolify deployment. Configure environment variables through the Coolify interface. + +## Project Structure + +``` +. +├── app/ +│ ├── api/upload/route.ts # Upload API + git push +│ ├── globals.css +│ ├── layout.tsx +│ └── page.tsx +├── components/ +│ └── UploadZone.tsx # Upload component +├── public/ +│ ├── models/ # Uploaded 3D models +│ └── textures/ # Uploaded textures +├── Dockerfile +├── package.json +├── tailwind.config.ts +└── tsconfig.json +``` + +## Supported File Formats + +| Type | Extensions | +|------|------------| +| 3D Models | .glb, .gltf, .fbx | +| Textures | .png, .jpg, .jpeg, .webp, .ktx2 | + +## License + +MIT \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index 37ae145..688a985 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,4 +1,5 @@ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap'); @tailwind base; @tailwind components; @@ -10,16 +11,6 @@ } body { - @apply bg-surface-soft text-gray-800 antialiased; + @apply bg-black-900 text-gray-100 antialiased; } -} - -@layer utilities { - .text-gradient { - @apply bg-gradient-to-r from-brand-500 to-rose-400 bg-clip-text text-transparent; - } - - .bg-gradient-brand { - @apply bg-gradient-to-r from-brand-500 to-rose-400; - } -} +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index dd42840..bff7b8f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,33 +2,26 @@ import UploadZone from '@/components/UploadZone' export default function Home() { return ( -
- {/* Header */} +
-
- - Git LFS · Déploiement continu -
-

- Asset Bridge 3D +

+ Upload GLTF

-

- Déposez vos fichiers 3D — ils seront automatiquement versionnés -
et poussés sur le dépôt GitHub via Git LFS. +

+ Drop your 3D files — they will be automatically versioned +
and pushed to your GitHub repository via Git LFS.

- {/* Upload Zone */} - {/* Footer */} -
- Modèles : .glb · .gltf · .fbx +
+ Models: .glb · .gltf · .fbx · - Textures : .png · .jpg · .webp · .ktx2 + Textures: .png · .jpg · .webp · .ktx2 · - Taille max : 2 GB + Max size: 2 GB
) -} +} \ No newline at end of file diff --git a/components/UploadZone.tsx b/components/UploadZone.tsx index 54c2516..bb22aee 100644 --- a/components/UploadZone.tsx +++ b/components/UploadZone.tsx @@ -3,9 +3,6 @@ import { useCallback, useRef, useState } from 'react' import { useDropzone, FileRejection } from 'react-dropzone' -// ───────────────────────────────────────────── -// Types -// ───────────────────────────────────────────── type FileStatus = 'pending' | 'uploading' | 'success' | 'error' interface FileEntry { @@ -25,9 +22,6 @@ interface UploadResult { gitError?: string } -// ───────────────────────────────────────────── -// Formats acceptés -// ───────────────────────────────────────────── const ACCEPTED_FORMATS = { 'model/gltf-binary': ['.glb'], 'model/gltf+json': ['.gltf'], @@ -80,12 +74,12 @@ function uploadSingleFile( try { resolve(JSON.parse(xhr.responseText)) } catch { - resolve({ success: false, error: `Réponse inattendue (HTTP ${xhr.status})` }) + resolve({ success: false, error: `Unexpected response (HTTP ${xhr.status})` }) } } - xhr.onerror = () => resolve({ success: false, error: 'Erreur réseau.' }) - xhr.onabort = () => resolve({ success: false, error: 'Annulé.' }) + xhr.onerror = () => resolve({ success: false, error: 'Network error.' }) + xhr.onabort = () => resolve({ success: false, error: 'Cancelled.' }) xhr.open('POST', '/api/upload') xhr.setRequestHeader('x-upload-secret', secret.trim()) @@ -93,9 +87,6 @@ function uploadSingleFile( }) } -// ───────────────────────────────────────────── -// Composant principal -// ───────────────────────────────────────────── export default function UploadZone() { const [files, setFiles] = useState([]) const [isUploading, setIsUploading] = useState(false) @@ -109,10 +100,9 @@ export default function UploadZone() { setFiles((prev) => prev.map((f, i) => i === index ? { ...f, ...patch } : f)) } - // ── Upload séquentiel de tous les fichiers ── const handleUpload = useCallback(async () => { if (!secret.trim()) { - setGlobalError('Veuillez saisir la clé d\'accès avant d\'uploader.') + setGlobalError('Please enter the access key before uploading.') return } if (files.length === 0) return @@ -144,38 +134,33 @@ export default function UploadZone() { setIsUploading(false) }, [files, secret, assetName]) - // ── Annuler le fichier en cours ── const handleCancel = () => { xhrRef.current?.abort() } - // ── Supprimer un fichier de la liste ── const removeFile = (index: number) => { setFiles((prev) => prev.filter((_, i) => i !== index)) } - // ── Reset total ── const handleReset = () => { setFiles([]) setGlobalError(null) setIsUploading(false) } - // ── react-dropzone ── const onDrop = useCallback((acceptedFiles: File[], rejectedFiles: FileRejection[]) => { if (rejectedFiles.length > 0) { const codes = rejectedFiles[0].errors.map((e) => e.code).join(', ') setGlobalError( codes.includes('file-invalid-type') - ? `Format non supporté. Utilisez : ${ALL_EXTENSIONS.join(', ')}` + ? `Unsupported format. Use: ${ALL_EXTENSIONS.join(', ')}` : codes.includes('file-too-large') - ? 'Fichier trop volumineux (max 2 GB)' - : `Fichier rejeté : ${codes}` + ? 'File too large (max 2 GB)' + : `File rejected: ${codes}` ) return } setGlobalError(null) setFiles((prev) => { - // Dédoublonner par nom const existingNames = new Set(prev.map((f) => f.file.name)) const newEntries: FileEntry[] = acceptedFiles .filter((f) => !existingNames.has(f.name)) @@ -195,31 +180,26 @@ export default function UploadZone() { const allDone = files.length > 0 && files.every((f) => f.status === 'success') const hasErrors = files.some((f) => f.status === 'error') - // ───────────────────────────────────────────── - // Render - // ───────────────────────────────────────────── return (
- - {/* Clé d'accès */}
- +
setSecret(e.target.value)} - placeholder="Saisir la clé secrète…" + placeholder="Enter secret key..." disabled={isUploading} - className="w-full bg-white border border-surface-border rounded-xl px-4 py-2.5 pr-12 - text-gray-800 placeholder-gray-300 text-sm shadow-soft - focus:outline-none focus:ring-2 focus:ring-brand-300 focus:border-brand-400 + className="w-full bg-black-800 border border-black-700 rounded-xl px-4 py-2.5 pr-12 + text-gray-100 placeholder-gray-500 text-sm + focus:outline-none focus:ring-2 focus:ring-gray-600 focus:border-gray-500 disabled:opacity-50 disabled:cursor-not-allowed transition" />
- {/* Nom de l'asset */}
-
- {/* Dropzone */}
- + @@ -280,102 +258,96 @@ export default function UploadZone() {
{isDragReject ? ( -

Format non supporté

+

Unsupported format

) : isDragActive ? ( -

Relâchez pour ajouter

+

Release to add

) : ( <> -

- Glissez vos fichiers ici - ou cliquez pour parcourir +

+ Drag files here + or click to browse

-

- Modèles : .glb, .gltf, .fbx · Textures : .png, .jpg, .webp, .ktx2 +

+ Models: .glb, .gltf, .fbx · Textures: .png, .jpg, .webp, .ktx2

)}
- {/* Erreur globale */} {globalError && ( -

{globalError}

+

{globalError}

)} - {/* Liste des fichiers */} {files.length > 0 && (
{files.map((entry, i) => { const type = getFileType(entry.file.name) return (
+ className="flex items-center gap-3 bg-black-800 border border-black-700 rounded-xl px-4 py-3"> - {/* Icône statut */}
{entry.status === 'success' ? ( -
- +
+
) : entry.status === 'error' ? ( -
- +
+
) : entry.status === 'uploading' ? ( -
- +
+
) : ( -
- +
+
)}
- {/* Infos fichier */}
- {entry.file.name} + {entry.file.name} {type && ( + ${type === 'model' ? 'bg-gray-700 text-gray-300' : 'bg-gray-700 text-gray-300'}`}> {type === 'model' ? '3D' : 'Texture'} )}
- {formatBytes(entry.file.size)} + {formatBytes(entry.file.size)} {entry.status === 'error' && entry.error && ( {entry.error} )} {entry.status === 'success' && entry.filename && ( - {entry.filename} + {entry.filename} )}
- {/* Barre de progression individuelle */} {entry.status === 'uploading' && ( -
+
)}
- {/* Supprimer */} {entry.status !== 'uploading' && (
)} - {/* Boutons d'action */}
{!isUploading && files.some((f) => f.status === 'pending' || f.status === 'error') && ( )} {isUploading && ( )} {(allDone || hasErrors) && !isUploading && ( )}
) -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 17f085e..4a3afb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,13 +15,13 @@ }, "devDependencies": { "@types/busboy": "^1.5.4", - "@types/node": "^20.14.0", - "@types/react": "^18.3.0", + "@types/node": "20.19.37", + "@types/react": "18.3.28", "@types/react-dom": "^18.3.0", "autoprefixer": "^10.4.19", "postcss": "^8.4.38", "tailwindcss": "^3.4.4", - "typescript": "^5.4.5" + "typescript": "5.9.3" } }, "node_modules/@alloc/quick-lru": { @@ -38,9 +38,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", - "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "license": "MIT", "optional": true, "dependencies": { @@ -140,6 +140,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -156,6 +159,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -172,6 +178,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -188,6 +197,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -204,6 +216,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -220,6 +235,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -236,6 +254,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -252,6 +273,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -268,6 +292,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -290,6 +317,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -312,6 +342,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -334,6 +367,9 @@ "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -356,6 +392,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -378,6 +417,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -400,6 +442,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -422,6 +467,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ @@ -553,15 +601,15 @@ } }, "node_modules/@next/env": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.7.tgz", - "integrity": "sha512-rJJbIdJB/RQr2F1nylZr/PJzamvNNhfr3brdKP6s/GW850jbtR70QlSfFselvIBbcPUOlQwBakexjFzqLzF6pg==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.2.tgz", + "integrity": "sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.7.tgz", - "integrity": "sha512-b2wWIE8sABdyafc4IM8r5Y/dS6kD80JRtOGrUiKTsACFQfWWgUQ2NwoUX1yjFMXVsAwcQeNpnucF2ZrujsBBPg==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.2.tgz", + "integrity": "sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==", "cpu": [ "arm64" ], @@ -575,9 +623,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.7.tgz", - "integrity": "sha512-zcnVaaZulS1WL0Ss38R5Q6D2gz7MtBu8GZLPfK+73D/hp4GFMrC2sudLky1QibfV7h6RJBJs/gOFvYP0X7UVlQ==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.2.tgz", + "integrity": "sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==", "cpu": [ "x64" ], @@ -591,12 +639,15 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.7.tgz", - "integrity": "sha512-2ant89Lux/Q3VyC8vNVg7uBaFVP9SwoK2jJOOR0L8TQnX8CAYnh4uctAScy2Hwj2dgjVHqHLORQZJ2wH6VxhSQ==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.2.tgz", + "integrity": "sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -607,12 +658,15 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.7.tgz", - "integrity": "sha512-uufcze7LYv0FQg9GnNeZ3/whYfo+1Q3HnQpm16o6Uyi0OVzLlk2ZWoY7j07KADZFY8qwDbsmFnMQP3p3+Ftprw==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.2.tgz", + "integrity": "sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -623,12 +677,15 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.7.tgz", - "integrity": "sha512-KWVf2gxYvHtvuT+c4MBOGxuse5TD7DsMFYSxVxRBnOzok/xryNeQSjXgxSv9QpIVlaGzEn/pIuI6Koosx8CGWA==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.2.tgz", + "integrity": "sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -639,12 +696,15 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.7.tgz", - "integrity": "sha512-HguhaGwsGr1YAGs68uRKc4aGWxLET+NevJskOcCAwXbwj0fYX0RgZW2gsOCzr9S11CSQPIkxmoSbuVaBp4Z3dA==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.2.tgz", + "integrity": "sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -655,9 +715,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.7.tgz", - "integrity": "sha512-S0n3KrDJokKTeFyM/vGGGR8+pCmXYrjNTk2ZozOL1C/JFdfUIL9O1ATaJOl5r2POe56iRChbsszrjMAdWSv7kQ==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.2.tgz", + "integrity": "sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==", "cpu": [ "arm64" ], @@ -671,9 +731,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.7.tgz", - "integrity": "sha512-mwgtg8CNZGYm06LeEd+bNnOUfwOyNem/rOiP14Lsz+AnUY92Zq/LXwtebtUiaeVkhbroRCQ0c8GlR4UT1U+0yg==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.2.tgz", + "integrity": "sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==", "cpu": [ "x64" ], @@ -856,9 +916,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.8", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", - "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.13.tgz", + "integrity": "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -894,9 +954,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -914,11 +974,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -938,9 +998,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001780", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", - "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "version": "1.0.30001784", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", + "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", "funding": [ { "type": "opencollective", @@ -1056,9 +1116,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.321", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz", - "integrity": "sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==", + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", "dev": true, "license": "ISC" }, @@ -1367,12 +1427,12 @@ } }, "node_modules/next": { - "version": "16.1.7", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.7.tgz", - "integrity": "sha512-WM0L7WrSvKwoLegLYr6V+mz+RIofqQgVAfHhMp9a88ms0cFX8iX9ew+snpWlSBwpkURJOUdvCEt3uLl3NNzvWg==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.2.tgz", + "integrity": "sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==", "license": "MIT", "dependencies": { - "@next/env": "16.1.7", + "@next/env": "16.2.2", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -1386,15 +1446,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.7", - "@next/swc-darwin-x64": "16.1.7", - "@next/swc-linux-arm64-gnu": "16.1.7", - "@next/swc-linux-arm64-musl": "16.1.7", - "@next/swc-linux-x64-gnu": "16.1.7", - "@next/swc-linux-x64-musl": "16.1.7", - "@next/swc-win32-arm64-msvc": "16.1.7", - "@next/swc-win32-x64-msvc": "16.1.7", - "sharp": "^0.34.4" + "@next/swc-darwin-arm64": "16.2.2", + "@next/swc-darwin-x64": "16.2.2", + "@next/swc-linux-arm64-gnu": "16.2.2", + "@next/swc-linux-arm64-musl": "16.2.2", + "@next/swc-linux-x64-gnu": "16.2.2", + "@next/swc-linux-x64-musl": "16.2.2", + "@next/swc-win32-arm64-msvc": "16.2.2", + "@next/swc-win32-x64-msvc": "16.2.2", + "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -1448,9 +1508,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", "dev": true, "license": "MIT" }, @@ -1497,9 +1557,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -2083,9 +2143,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 92209c1..8edd10f 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,12 @@ }, "devDependencies": { "@types/busboy": "^1.5.4", - "@types/node": "^20.14.0", - "@types/react": "^18.3.0", + "@types/node": "20.19.37", + "@types/react": "18.3.28", "@types/react-dom": "^18.3.0", "autoprefixer": "^10.4.19", "postcss": "^8.4.38", "tailwindcss": "^3.4.4", - "typescript": "^5.4.5" + "typescript": "5.9.3" } } diff --git a/tailwind.config.ts b/tailwind.config.ts index 3b1211f..3267f37 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -9,28 +9,29 @@ const config: Config = { theme: { extend: { colors: { - brand: { - 50: '#FAF5FF', - 100: '#EDE9FE', - 200: '#DDD6FE', - 300: '#C4B5FD', - 400: '#A78BFA', - 500: '#7C5CFC', // violet principal (image) - 600: '#6D28D9', - 700: '#5B21B6', - 900: '#2E1065', + black: { + 50: '#F7F7F7', + 100: '#E5E5E5', + 200: '#CCCCCC', + 300: '#999999', + 400: '#666666', + 500: '#333333', + 600: '#1A1A1A', + 700: '#0D0D0D', + 800: '#050505', + 900: '#000000', }, - // Rose/magenta (dégradé image) - rose: { - 300: '#FDA4C4', - 400: '#F472B6', - 500: '#EC4899', - }, - surface: { - DEFAULT: '#FFFFFF', - soft: '#F9F5FF', // fond global lavande très clair - muted: '#F3EEFF', // fond carte/panel - border: '#E5D9FD', // bordure douce + gray: { + 50: '#FAFAFA', + 100: '#F5F5F5', + 200: '#E5E5E5', + 300: '#D4D4D4', + 400: '#A3A3A3', + 500: '#737373', + 600: '#525252', + 700: '#404040', + 800: '#262626', + 900: '#171717', }, }, fontFamily: { @@ -38,12 +39,12 @@ const config: Config = { mono: ['JetBrains Mono', 'Fira Code', 'monospace'], }, boxShadow: { - soft: '0 2px 16px 0 rgba(124, 92, 252, 0.08)', - card: '0 4px 24px 0 rgba(124, 92, 252, 0.10)', + soft: '0 2px 16px 0 rgba(0, 0, 0, 0.06)', + card: '0 4px 24px 0 rgba(0, 0, 0, 0.08)', }, }, }, plugins: [], } -export default config +export default config \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 9e9bbf7..f15eefe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ {