Docfile commited on
Commit
3ea01fd
·
verified ·
1 Parent(s): fb233ec

Update templates/svt.html

Browse files
Files changed (1) hide show
  1. templates/svt.html +349 -361
templates/svt.html CHANGED
@@ -3,426 +3,414 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Mariam AI - SVT</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
10
- <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  <style>
12
- @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
13
-
14
  body {
15
- font-family: 'Poppins', sans-serif;
16
- background: linear-gradient(135deg, #f6f8fc 0%, #e9f0f7 100%);
17
  }
18
 
19
- .animate-fade-in {
20
- animation: fadeIn 0.5s ease-in;
 
 
21
  }
22
-
23
- .animate-slide-up {
24
- animation: slideUp 0.5s ease-out;
 
25
  }
26
-
27
- @keyframes fadeIn {
28
- from { opacity: 0; }
29
- to { opacity: 1; }
 
 
 
 
30
  }
31
 
32
- @keyframes slideUp {
33
- from { transform: translateY(20px); opacity: 0; }
34
- to { transform: translateY(0); opacity: 1; }
 
 
 
 
 
 
35
  }
36
 
37
- .glass-effect {
38
- background: rgba(255, 255, 255, 0.95);
39
- backdrop-filter: blur(10px);
40
- border: 1px solid rgba(255, 255, 255, 0.2);
41
  }
42
 
43
- .hover-scale {
44
- transition: transform 0.2s;
 
45
  }
46
 
47
- .hover-scale:hover {
48
- transform: scale(1.02);
 
 
49
  }
50
 
51
- .image-preview {
52
- transition: all 0.3s ease;
 
 
53
  }
54
 
55
- .image-preview:hover .image-overlay {
 
56
  opacity: 1;
57
  }
58
 
59
- .image-overlay {
60
- opacity: 0;
61
- transition: opacity 0.3s ease;
62
- background: rgba(0, 0, 0, 0.5);
 
63
  }
64
 
65
- .preview-container {
66
- display: grid;
67
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
68
- gap: 1rem;
69
  }
70
 
71
- /* Responsive adjustments */
72
- @media (max-width: 640px) {
73
- .preview-container {
74
- grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
75
- }
76
- .image-preview img {
77
- height: 32 object-cover;
78
- }
79
- #historyContainer .grid {
80
- grid-template-columns: repeat(1, minmax(0, 1fr));
81
- }
 
82
  }
83
-
84
  </style>
85
  </head>
86
- <body class="min-h-screen flex items-center justify-center p-4 md:p-8">
87
- <div class="container mx-auto glass-effect rounded-2xl shadow-xl max-w-4xl animate-fade-in p-6 md:p-8">
88
- <header class="flex flex-col md:flex-row justify-between items-center mb-8">
89
- <div class="text-center md:text-left mb-4 md:mb-0">
90
- <h1 class="text-4xl font-bold text-blue-900">Mariam AI</h1>
91
- <p class="text-gray-600 mt-2">Assistant SVT Intelligent</p>
92
- </div>
93
- <button onclick="showInfo()" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-xl hover-scale transition-all duration-300 flex items-center gap-2">
94
- <i class="fas fa-info-circle"></i>
95
- <span>Guide d'utilisation</span>
96
- </button>
97
- </header>
98
-
99
- <div class="space-y-8 animate-slide-up">
100
- <div class="relative">
101
- <label for="svtOption" class="block mb-3 text-lg font-medium text-gray-700">Type d'exercice :</label>
102
- <select id="svtOption" class="w-full p-4 border border-gray-200 rounded-xl bg-white shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-300">
103
- <option value="Restitution organisée des connaissances">Restitution Organisée des Connaissances</option>
104
- <option value="Exploitation du document">Exploitation du Document</option>
105
- <option value="Synthèse">Synthèse</option>
106
- </select>
107
- </div>
108
-
109
- <div class="relative">
110
- <label class="block mb-3 text-lg font-medium text-gray-700">Images du sujet :</label>
111
- <div class="border-2 border-dashed border-gray-300 rounded-xl p-8 text-center hover:border-blue-500 transition-all duration-300">
112
- <input type="file" id="imageUpload" class="hidden" multiple accept="image/*" onchange="handleImageUpload(event)">
113
- <label for="imageUpload" class="cursor-pointer">
114
- <i class="fas fa-cloud-upload-alt text-4xl text-blue-500 mb-4"></i>
115
- <p class="text-gray-600">Glissez vos images ici ou cliquez pour sélectionner</p>
116
- <p class="text-sm text-gray-500 mt-2">Format acceptés : JPG, PNG, GIF</p>
117
- </label>
118
  </div>
119
-
120
- <!-- Conteneur des prévisualisations -->
121
- <div id="previewContainer" class="preview-container mt-4">
122
- <!-- Les prévisualisations seront ajoutées ici -->
 
 
123
  </div>
124
  </div>
125
-
126
- <button onclick="submitQuestion()" class="w-full bg-gradient-to-r from-blue-600 to-blue-800 text-white py-4 rounded-xl hover-scale transition-all duration-300 font-semibold text-lg flex items-center justify-center gap-2">
127
- <i class="fas fa-paper-plane"></i>
128
- Analyser
129
- </button>
130
-
131
- <div id="loader" class="hidden">
132
- <div class="flex flex-col items-center space-y-4 p-8">
133
- <div class="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
134
- <p class="text-gray-600 text-lg">Analyse en cours...</p>
135
- </div>
136
- </div>
137
-
138
- <div id="response" class="mt-6 p-6 bg-white rounded-xl shadow-sm prose max-w-none"></div>
139
-
140
- <div id="copyResponseContainer" class="hidden mb-8">
141
- <button onclick="copyResponse()" class="w-full bg-gray-800 hover:bg-gray-900 text-white px-6 py-3 rounded-xl hover-scale transition-all duration-300 flex items-center justify-center gap-2">
142
- <i class="fas fa-copy"></i>
143
- Copier la réponse
144
- </button>
145
- </div>
146
  </div>
147
-
148
- <!-- Historique des réponses déplacé ici -->
149
- <div class="space-y-8 animate-slide-up">
150
- <h2 class="text-2xl font-bold text-blue-900 mb-4">Historique des Réponses</h2>
151
- <button onclick="clearLocalStorage()" class="bg-red-500 hover:bg-red-700 text-white px-4 py-2 rounded-lg">
152
- Effacer l'historique
153
- </button>
154
- <div id="historyContainer">
155
- <!-- L'historique des réponses sera affiché ici -->
 
 
 
 
 
156
  </div>
157
  </div>
158
- </div>
159
-
160
-
161
-
162
- <script>
163
- let uploadedFiles = [];
164
-
165
- function handleImageUpload(event) {
166
- const files = event.target.files;
167
- const previewContainer = document.getElementById('previewContainer');
168
-
169
- for (let i = 0; i < files.length; i++) {
170
- const file = files[i];
171
- uploadedFiles.push(file);
172
-
173
- const reader = new FileReader();
174
- reader.onload = function(e) {
175
- const imageId = `img-${Date.now()}-${i}`;
176
- const previewDiv = document.createElement('div');
177
- previewDiv.className = 'image-preview relative rounded-lg overflow-hidden';
178
- previewDiv.id = imageId;
179
- previewDiv.innerHTML = `
180
- <img src="${e.target.result}" alt="${file.name}" class="w-full h-40 object-cover">
181
- <div class="image-overlay absolute inset-0 flex items-center justify-center">
182
- <button onclick="removeImage('${imageId}')" class="bg-red-500 hover:bg-red-600 text-white p-2 rounded-full transition-all duration-300">
183
- <i class="fas fa-trash"></i>
184
- </button>
185
- <button onclick="previewImage('${e.target.result}')" class="bg-blue-500 hover:bg-blue-600 text-white p-2 rounded-full ml-2 transition-all duration-300">
186
- <i class="fas fa-eye"></i>
187
- </button>
 
 
 
 
188
  </div>
189
- <div class="bg-black bg-opacity-50 text-white text-xs p-1 absolute bottom-0 left-0 right-0">
190
- ${file.name.substring(0, 15)}${file.name.length > 15 ? '...' : ''}
 
191
  </div>
192
- `;
193
- previewContainer.appendChild(previewDiv);
194
- };
195
- reader.readAsDataURL(file);
196
- }
197
- }
198
-
199
- function removeImage(imageId) {
200
- const imageIndex = uploadedFiles.findIndex(file => {
201
- const fileId = `img-${file.lastModified}-${uploadedFiles.indexOf(file)}`;
202
- return fileId === imageId;
203
- });
204
-
205
- if (imageIndex !== -1) {
206
- uploadedFiles.splice(imageIndex, 1);
207
- }
208
-
209
- const element = document.getElementById(imageId);
210
- if (element) {
211
- element.remove();
212
- }
213
- }
214
-
215
- function previewImage(src) {
216
- Swal.fire({
217
- imageUrl: src,
218
- imageAlt: 'Prévisualisation',
219
- width: '80%',
220
- showConfirmButton: false,
221
- showCloseButton: true,
222
- customClass: {
223
- image: 'max-h-[80vh] object-contain'
224
- }
225
- });
226
- }
227
-
228
- function showInfo() {
229
- Swal.fire({
230
- title: 'Guide d\'utilisation',
231
- html: `
232
- <div class="text-left space-y-4">
233
- <div class="flex items-start gap-3">
234
- <i class="fas fa-check-circle text-green-500 mt-1"></i>
235
- <p>Sélectionnez le type d'exercice correspondant à votre sujet.</p>
236
  </div>
237
- <div class="flex items-start gap-3">
238
- <i class="fas fa-image text-blue-500 mt-1"></i>
239
- <p>Assurez-vous que vos images sont nettes et bien cadrées.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  </div>
241
- <div class="flex items-start gap-3">
242
- <i class="fas fa-crop-alt text-purple-500 mt-1"></i>
243
- <p>Rognez vos images pour ne garder que l'essentiel du sujet.</p>
 
244
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  </div>
246
- `,
247
- icon: 'info',
248
- confirmButtonText: 'Compris',
249
- confirmButtonColor: '#2563eb',
250
- customClass: {
251
- container: 'font-sans'
252
- }
253
- });
254
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
 
256
- function copyResponse() {
257
- const responseDiv = document.getElementById('response');
258
- const range = document.createRange();
259
- range.selectNode(responseDiv);
260
- window.getSelection().removeAllRanges();
261
- window.getSelection().addRange(range);
262
- document.execCommand('copy');
263
- window.getSelection().removeAllRanges();
264
-
265
- Swal.fire({
266
- icon: 'success',
267
- title: 'Copié !',
268
- text: 'La réponse a été copiée dans le presse-papiers.',
269
- showConfirmButton: false,
270
- timer: 1500,
271
- customClass: {
272
- popup: 'animate-fade-in'
273
- }
 
 
 
 
274
  });
275
- }
276
 
277
- async function submitQuestion() {
278
- if (uploadedFiles.length === 0) {
279
- Swal.fire({
280
- icon: 'error',
281
- title: 'Images manquantes',
282
- text: 'Veuillez sélectionner au moins une image du sujet.',
283
- confirmButtonColor: '#2563eb'
284
- });
285
- return;
286
- }
287
 
288
- const option = document.getElementById('svtOption').value;
289
- const loader = document.getElementById('loader');
290
- const responseDiv = document.getElementById('response');
291
- const copyResponseContainer = document.getElementById('copyResponseContainer');
292
-
293
- loader.classList.remove('hidden');
294
- responseDiv.innerHTML = '';
295
- copyResponseContainer.classList.add('hidden');
296
 
297
- const formData = new FormData();
298
- formData.append('option', option);
299
-
300
- for (let i = 0; i < uploadedFiles.length; i++) {
301
- formData.append('images', uploadedFiles[i]);
302
  }
303
 
304
- const response = await fetch('/svt_submit', {
305
- method: 'POST',
306
- body: formData
307
- });
308
-
309
- const data = await response.json();
310
- loader.classList.add('hidden');
311
-
312
- if (data.error) {
313
- responseDiv.innerHTML = `
314
- <div class="bg-red-50 border-l-4 border-red-500 p-4 rounded">
315
- <p class="text-red-700">Erreur : ${data.error}</p>
316
- </div>
317
- `;
318
  } else {
319
- const htmlContent = marked.parse(data.response);
320
- responseDiv.innerHTML = htmlContent;
321
- responseDiv.classList.add('animate-fade-in');
322
- copyResponseContainer.classList.remove('hidden');
323
-
324
- // Convertir les images en base64 avant de les sauvegarder
325
- const imagesData = await Promise.all(uploadedFiles.map(file => {
326
- return new Promise((resolve) => {
327
- const reader = new FileReader();
328
- reader.onload = (e) => resolve(e.target.result);
329
- reader.readAsDataURL(file);
330
- });
331
- }));
332
-
333
- saveResponseToLocalStorage(option, imagesData, data.response);
334
- displayHistory();
335
  }
336
- }
337
 
338
- // Fonctions pour la gestion du localStorage
339
- function saveResponseToLocalStorage(option, images, response) {
340
- const timestamp = new Date().toISOString();
341
- const data = { option, images, response, timestamp };
342
- localStorage.setItem('svt_response_' + timestamp, JSON.stringify(data));
343
- }
344
 
345
- function loadResponsesFromLocalStorage() {
346
- const responses = [];
347
- for (let i = 0; i < localStorage.length; i++) {
348
- const key = localStorage.key(i);
349
- if (key.startsWith('svt_response_')) {
350
- const data = JSON.parse(localStorage.getItem(key));
351
- responses.push(data);
352
- }
353
- }
354
- return responses.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
355
- }
356
 
357
- function clearLocalStorage() {
358
- const keysToRemove = [];
359
- for (let i = 0; i < localStorage.length; i++) {
360
- const key = localStorage.key(i);
361
- if (key.startsWith('svt_response_')) {
362
- keysToRemove.push(key);
363
- }
364
- }
365
- for (const key of keysToRemove) {
366
- localStorage.removeItem(key);
367
  }
368
- displayHistory();
369
- }
370
 
371
- function displayHistory() {
372
- const responses = loadResponsesFromLocalStorage();
373
- const historyContainer = document.getElementById('historyContainer');
374
- historyContainer.innerHTML = '';
375
 
376
- if (responses.length === 0) {
377
- historyContainer.innerHTML = '<p class="text-gray-500">Aucun historique disponible.</p>';
378
- return;
379
- }
380
 
381
- const responseList = document.createElement('ul');
382
- responseList.className = 'grid gap-4 md:grid-cols-2';
383
-
384
- responses.forEach(response => {
385
- const listItem = document.createElement('li');
386
- listItem.className = 'bg-white p-4 rounded-lg shadow-md hover:shadow-lg transition duration-300';
387
-
388
- const title = document.createElement('h4');
389
- title.className = 'text-lg font-semibold text-blue-800 mb-2';
390
- title.textContent = `${response.option} - ${new Date(response.timestamp).toLocaleString()}`;
391
- listItem.appendChild(title);
392
-
393
- const previewContainer = document.createElement('div');
394
- previewContainer.className = 'flex gap-2 mb-2';
395
- response.images.forEach(imageData => {
396
- const img = document.createElement('img');
397
- img.src = imageData;
398
- img.className = 'h-12 w-12 object-cover rounded-md cursor-pointer';
399
- img.onclick = () => previewImage(imageData);
400
- previewContainer.appendChild(img);
401
- });
402
- listItem.appendChild(previewContainer);
403
-
404
- const responsePreview = document.createElement('p');
405
- responsePreview.className = 'text-gray-600 text-sm';
406
- responsePreview.textContent = response.response.substring(0, 200) + (response.response.length > 200 ? '...' : '');
407
- listItem.appendChild(responsePreview);
408
-
409
- const viewButton = document.createElement('button');
410
- viewButton.className = 'bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg mt-2 text-sm';
411
- viewButton.textContent = 'Voir';
412
- viewButton.onclick = () => {
413
- document.getElementById('response').innerHTML = marked.parse(response.response);
414
- document.getElementById('copyResponseContainer').classList.remove('hidden');
415
- window.scrollTo({ top: document.getElementById('response').offsetTop, behavior: 'smooth' });
416
- };
417
- listItem.appendChild(viewButton);
418
-
419
- responseList.appendChild(listItem);
420
- });
421
 
422
- historyContainer.appendChild(responseList);
423
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
- displayHistory();
 
 
 
 
 
 
426
  </script>
427
  </body>
428
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Analyse Satellite Avancée</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+ <script>
12
+ tailwind.config = {
13
+ theme: {
14
+ extend: {
15
+ colors: {
16
+ 'primary-camo': '#5a6249', // Vert olive foncé
17
+ 'secondary-camo': '#1e293b', // Comme avant
18
+ 'camo-green': '#4a5538', // Vert olive moyen
19
+ 'camo-brown': '#6d5835', // Brun terre
20
+ 'camo-tan': '#d2b48c', // Beige sable (facultatif, pour un contraste plus clair)
21
+ },
22
+ animation: {
23
+ 'gradient': 'gradient 8s linear infinite',
24
+ 'spinner': 'spin 1.5s linear infinite',
25
+ },
26
+ keyframes: {
27
+ gradient: {
28
+ '0%, 100%': {
29
+ 'background-size': '200% 200%',
30
+ 'background-position': 'left center'
31
+ },
32
+ '50%': {
33
+ 'background-size': '200% 200%',
34
+ 'background-position': 'right center'
35
+ }
36
+ },
37
+ spin: {
38
+ '0%': { transform: 'rotate(0deg)' },
39
+ '100%': { transform: 'rotate(360deg)' }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ </script>
46
  <style>
47
+ /* Camouflage Background */
 
48
  body {
49
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Cg fill-rule='evenodd'%3E%3Cg fill='%235a6249' fill-opacity='0.4'%3E%3Cpath opacity='.5' d='M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z'/%3E%3Cpath d='M6 5V0H5v5H0v1h5v94h1V6h94V5H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
50
+ background-size: 200px 200px; /* Ajustez la taille du motif selon vos préférences */
51
  }
52
 
53
+ .gradient-animate {
54
+ background: linear-gradient(270deg, #5a6249, #4a5538);
55
+ background-size: 200% 200%;
56
+ animation: gradient 8s ease infinite;
57
  }
58
+
59
+ .card-hover {
60
+ transition: transform 0.3s ease-in-out;
61
+ background-color: rgba(255, 255, 255, 0.8); /* Légère transparence pour les cartes */
62
  }
63
+
64
+ .card-hover:hover {
65
+ transform: translateY(-5px);
66
+ }
67
+
68
+ .progress-bar {
69
+ width: 0%;
70
+ transition: width 0.5s ease-in-out;
71
  }
72
 
73
+ .loader {
74
+ border: 8px solid #f3f3f3;
75
+ border-top: 8px solid #5a6249;
76
+ border-radius: 50%;
77
+ width: 60px;
78
+ height: 60px;
79
+ animation: spin 1.5s linear infinite;
80
+ display: none; /* Initially hidden */
81
+ margin: 20px auto;
82
  }
83
 
84
+ @keyframes spin {
85
+ 0% { transform: rotate(0deg); }
86
+ 100% { transform: rotate(360deg); }
 
87
  }
88
 
89
+ .fade-in {
90
+ opacity: 0;
91
+ animation: fadeIn 1s ease-in-out forwards;
92
  }
93
 
94
+ @keyframes fadeIn {
95
+ to {
96
+ opacity: 1;
97
+ }
98
  }
99
 
100
+ .slide-up {
101
+ transform: translateY(50px);
102
+ opacity: 0;
103
+ transition: transform 0.8s ease-out, opacity 0.8s ease-out;
104
  }
105
 
106
+ .slide-up.active {
107
+ transform: translateY(0);
108
  opacity: 1;
109
  }
110
 
111
+ #imagePreview {
112
+ max-width: 100%;
113
+ max-height: 300px;
114
+ margin-top: 20px;
115
+ display: none;
116
  }
117
 
118
+ /* Style pour les éléments de texte importants */
119
+ .text-camo-green {
120
+ color: #4a5538;
 
121
  }
122
 
123
+ .text-camo-brown {
124
+ color: #6d5835;
125
+ }
126
+
127
+ .text-camo-tan {
128
+ color: #d2b48c;
129
+ }
130
+ nav, footer{
131
+ background-color: #5a6249 !important;
132
+ }
133
+ nav span, footer h3, footer a, footer p{
134
+ color: #d2b48c !important;
135
  }
 
136
  </style>
137
  </head>
138
+ <body class="min-h-screen">
139
+ <!-- Barre de Navigation -->
140
+ <nav class="shadow-lg fixed w-full z-50">
141
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
142
+ <div class="flex justify-between h-16">
143
+ <div class="flex items-center">
144
+ <span class="text-xl font-bold">SatelliteAI</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  </div>
146
+ <div class="flex items-center space-x-4">
147
+ <a href="https://github.com/Yusufibin" target="_blank"
148
+ class="flex items-center space-x-2 hover:text-primary-camo transition-colors">
149
+ <i class="fab fa-github text-xl"></i>
150
+ <span>GitHub</span>
151
+ </a>
152
  </div>
153
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  </div>
155
+ </nav>
156
+
157
+ <!-- Hero Section -->
158
+ <header class="pt-24 pb-12 gradient-animate">
159
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-camo-tan">
160
+ <h1 class="text-4xl font-bold mb-4">Reconnaissance d'Objets Satellite</h1>
161
+ <p class="text-xl mb-8">Analyse avancée d'images satellite alimentée par l'IA</p>
162
+ <div class="flex justify-center space-x-4">
163
+ <a href="#upload" class="bg-camo-tan text-primary-camo px-6 py-3 rounded-lg font-medium hover:bg-camo-brown hover:text-camo-tan transition-colors">
164
+ Commencer l'analyse
165
+ </a>
166
+ <a href="#about" class="border border-camo-tan text-camo-tan px-6 py-3 rounded-lg font-medium hover:bg-camo-tan hover:text-primary-camo transition-colors">
167
+ En savoir plus
168
+ </a>
169
  </div>
170
  </div>
171
+ </header>
172
+
173
+ <main class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
174
+ <!-- Section À propos -->
175
+ <section id="about" class="mb-16">
176
+ <div class="grid md:grid-cols-2 gap-8">
177
+ <div class="card-hover bg-white rounded-xl shadow-lg p-6 slide-up">
178
+ <h2 class="text-2xl font-bold text-camo-green mb-4">À propos du projet</h2>
179
+ <p class="text-camo-brown mb-4">
180
+ Ce système utilise l'API Gemini pour analyser des images satellite et détecter automatiquement les objets présents.
181
+ Il combine la puissance du deep learning avec une interface utilisateur intuitive pour fournir des analyses détaillées
182
+ et précises des images satellite.
183
+ </p>
184
+ <ul class="space-y-2 text-camo-brown">
185
+ <li class="flex items-center">
186
+ <i class="fas fa-check text-camo-green mr-2"></i>
187
+ Détection d'objets en temps réel
188
+ </li>
189
+ <li class="flex items-center">
190
+ <i class="fas fa-check text-camo-green mr-2"></i>
191
+ Analyse détaillée du contenu
192
+ </li>
193
+ <li class="flex items-center">
194
+ <i class="fas fa-check text-camo-green mr-2"></i>
195
+ Interface utilisateur moderne
196
+ </li>
197
+ </ul>
198
+ </div>
199
+ <div class="card-hover bg-white rounded-xl shadow-lg p-6 slide-up">
200
+ <h2 class="text-2xl font-bold text-camo-green mb-4">Technologies utilisées</h2>
201
+ <div class="grid grid-cols-2 gap-4">
202
+ <div class="flex items-center space-x-2">
203
+ <i class="fab fa-python text-2xl text-blue-500"></i>
204
+ <span class="text-camo-brown">Python/Flask</span>
205
  </div>
206
+ <div class="flex items-center space-x-2">
207
+ <i class="fab fa-js text-2xl text-yellow-500"></i>
208
+ <span class="text-camo-brown">JavaScript</span>
209
  </div>
210
+ <div class="flex items-center space-x-2">
211
+ <i class="fab fa-google text-2xl text-primary-camo"></i>
212
+ <span class="text-camo-brown">Gemini API</span>
213
+ </div>
214
+ <div class="flex items-center space-x-2">
215
+ <i class="fab fa-css3 text-2xl text-blue-400"></i>
216
+ <span class="text-camo-brown">Tailwind CSS</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </section>
222
+
223
+ <!-- Section Upload -->
224
+ <section id="upload" class="mb-16">
225
+ <div class="card-hover bg-white rounded-xl shadow-lg p-8 slide-up">
226
+ <h2 class="text-2xl font-bold text-camo-green mb-6">Analyse d'image</h2>
227
+ <div class="border-2 border-dashed border-gray-300 rounded-lg p-8 transition-colors hover:border-primary-camo">
228
+ <form id="uploadForm" class="space-y-6">
229
+ <div class="text-center">
230
+ <i class="fas fa-satellite text-5xl text-primary-camo mb-4"></i>
231
+ <div class="mt-4">
232
+ <label class="block text-lg font-medium text-camo-brown mb-2">
233
+ Sélectionner une image satellite
234
+ </label>
235
+ <input type="file" id="fileInput"
236
+ class="mt-1 block w-full text-sm text-gray-500
237
+ file:mr-4 file:py-2 file:px-4
238
+ file:rounded-full file:border-0
239
+ file:text-sm file:font-semibold
240
+ file:bg-primary-camo file:text-camo-tan
241
+ hover:file:bg-camo-green
242
+ transition-colors"
243
+ accept="image/*">
244
+ <img id="imagePreview" src="#" alt="Prévisualisation de l'image" />
245
+ </div>
246
  </div>
247
+ <div class="flex justify-center">
248
+ <button type="submit" class="bg-primary-camo text-camo-tan px-8 py-3 rounded-full font-semibold hover:bg-camo-green transition-colors">
249
+ Lancer l'analyse
250
+ </button>
251
  </div>
252
+ <div id="loader" class="loader"></div>
253
+ </form>
254
+ </div>
255
+ </div>
256
+ </section>
257
+
258
+ <!-- Section Résultats -->
259
+ <section id="results" class="opacity-0">
260
+ <div class="grid md:grid-cols-2 gap-8">
261
+ <div class="card-hover bg-white rounded-xl shadow-lg p-6 slide-up">
262
+ <h3 class="text-xl font-bold text-camo-green mb-4">Objets Détectés</h3>
263
+ <div id="detectedObjects" class="space-y-3">
264
+ <!-- Les résultats seront insérés ici -->
265
  </div>
266
+ </div>
267
+ <div class="card-hover bg-white rounded-xl shadow-lg p-6 slide-up">
268
+ <h3 class="text-xl font-bold text-camo-green mb-4">Description Détaillée</h3>
269
+ <div id="description" class="prose text-camo-brown">
270
+ <!-- La description sera insérée ici -->
271
+ </div>
272
+ </div>
273
+ </div>
274
+ </section>
275
+ </main>
276
+
277
+ <!-- Footer -->
278
+ <footer class="text-camo-tan py-8 mt-16">
279
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
280
+ <div class="flex justify-between items-center">
281
+ <div>
282
+ <h3 class="text-lg font-semibold">SatelliteAI</h3>
283
+ <p class="text-gray-400">Développé par Yusufibin</p>
284
+ </div>
285
+ <div>
286
+ <a href="https://github.com/Yusufibin" target="_blank"
287
+ class="hover:text-camo-brown transition-colors">
288
+ <i class="fab fa-github text-2xl"></i>
289
+ </a>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ </footer>
294
 
295
+ <script>
296
+ // Initialisation de GSAP ScrollTrigger
297
+ gsap.registerPlugin(ScrollTrigger);
298
+
299
+ // Animations au chargement
300
+ gsap.from("header", {
301
+ opacity: 0,
302
+ y: -50,
303
+ duration: 1,
304
+ ease: "power3.out"
305
+ });
306
+
307
+ // Animations au scroll
308
+ gsap.utils.toArray(".slide-up").forEach((el, index) => {
309
+ gsap.from(el, {
310
+ scrollTrigger: {
311
+ trigger: el,
312
+ start: "top bottom-=100",
313
+ onEnter: () => {
314
+ el.classList.add("active");
315
+ },
316
+ },
317
  });
318
+ });
319
 
320
+ // Prévisualisation de l'image
321
+ const fileInput = document.getElementById('fileInput');
322
+ const imagePreview = document.getElementById('imagePreview');
 
 
 
 
 
 
 
323
 
324
+ fileInput.addEventListener('change', function(e) {
325
+ const file = e.target.files[0];
326
+ const reader = new FileReader();
 
 
 
 
 
327
 
328
+ reader.onload = function(e) {
329
+ imagePreview.src = e.target.result;
330
+ imagePreview.style.display = 'block';
 
 
331
  }
332
 
333
+ if (file) {
334
+ reader.readAsDataURL(file);
 
 
 
 
 
 
 
 
 
 
 
 
335
  } else {
336
+ imagePreview.src = '#';
337
+ imagePreview.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  }
339
+ });
340
 
341
+ // Gestion du formulaire
342
+ document.getElementById('uploadForm').addEventListener('submit', async function(e) {
343
+ e.preventDefault();
 
 
 
344
 
345
+ const fileInput = document.getElementById('fileInput');
346
+ const loader = document.getElementById('loader');
347
+ const resultsSection = document.getElementById('results');
348
+ const detectedObjectsDiv = document.getElementById('detectedObjects');
349
+ const descriptionDiv = document.getElementById('description');
 
 
 
 
 
 
350
 
351
+ // Vérifier si un fichier a été sélectionné
352
+ if (!fileInput.files[0]) {
353
+ alert('Veuillez sélectionner une image.');
354
+ return;
 
 
 
 
 
 
355
  }
 
 
356
 
357
+ // Afficher le loader
358
+ loader.style.display = 'block';
 
 
359
 
360
+ // Simulation de l'analyse (à remplacer par votre API)
361
+ // Attendre 3 secondes pour simuler une analyse longue
362
+ await new Promise(resolve => setTimeout(resolve, 3000));
 
363
 
364
+ // Masquer le loader
365
+ loader.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
 
367
+ // Afficher la section résultats avec une animation
368
+ resultsSection.classList.add('fade-in');
369
+
370
+ // Simulation des résultats (à remplacer par les résultats de votre API)
371
+ detectedObjectsDiv.innerHTML = `
372
+ <div class="space-y-2 animate-pulse">
373
+ <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
374
+ <span class="text-camo-brown">Bâtiments</span>
375
+ <div class="w-32 bg-gray-200 rounded-full h-2">
376
+ <div class="bg-primary-camo h-2 rounded-full progress-bar" style="width: 85%"></div>
377
+ </div>
378
+ </div>
379
+ <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
380
+ <span class="text-camo-brown">Routes</span>
381
+ <div class="w-32 bg-gray-200 rounded-full h-2">
382
+ <div class="bg-primary-camo h-2 rounded-full progress-bar" style="width: 70%"></div>
383
+ </div>
384
+ </div>
385
+ <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
386
+ <span class="text-camo-brown">Végétation</span>
387
+ <div class="w-32 bg-gray-200 rounded-full h-2">
388
+ <div class="bg-primary-camo h-2 rounded-full progress-bar" style="width: 90%"></div>
389
+ </div>
390
+ </div>
391
+ <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
392
+ <span class="text-camo-brown">Eau</span>
393
+ <div class="w-32 bg-gray-200 rounded-full h-2">
394
+ <div class="bg-primary-camo h-2 rounded-full progress-bar" style="width: 60%"></div>
395
+ </div>
396
+ </div>
397
+ <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
398
+ <span class="text-camo-brown">Véhicules</span>
399
+ <div class="w-32 bg-gray-200 rounded-full h-2">
400
+ <div class="bg-primary-camo h-2 rounded-full progress-bar" style="width: 50%"></div>
401
+ </div>
402
+ </div>
403
+ </div>
404
+ `;
405
+ descriptionDiv.innerHTML = "<p class='text-camo-brown'>Analyse détaillée de l'image satellite. La détection a permis d'identifier plusieurs types d'objets avec une grande précision.</p>";
406
 
407
+ // Animer les barres de progression
408
+ setTimeout(() => {
409
+ document.querySelectorAll('.progress-bar').forEach(bar => {
410
+ bar.style.width = bar.parentElement.dataset.value || '75%';
411
+ });
412
+ }, 100);
413
+ });
414
  </script>
415
  </body>
416
  </html>