Docfile commited on
Commit
e6fa8ea
·
verified ·
1 Parent(s): 3de25c1

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +178 -45
templates/index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Résolveur d'Images - Mariam</title>
7
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.js"></script>
8
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/contrib/auto-render.min.js"></script>
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css">
@@ -13,6 +13,8 @@
13
  --primary-hover: #2980b9;
14
  --secondary-color: #2ecc71;
15
  --secondary-hover: #27ae60;
 
 
16
  --background-color: #f4f7f6;
17
  --text-color: #333;
18
  --border-color: #e0e0e0;
@@ -164,18 +166,44 @@
164
  display: none;
165
  }
166
 
167
- .preview-container {
168
  margin-top: var(--spacing-unit);
 
 
 
 
169
  }
170
 
171
- #image-preview {
172
- max-width: 100%;
173
- max-height: 300px;
174
- display: none;
175
- border-radius: 0.5rem;
 
176
  border: 1px solid var(--border-color);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
 
 
179
  .button {
180
  width: 100%;
181
  padding: var(--spacing-unit);
@@ -199,12 +227,21 @@
199
  background-color: #bdc3c7;
200
  cursor: not-allowed;
201
  }
 
 
 
 
 
 
 
 
 
202
 
203
  .copy-button {
204
  background-color: var(--secondary-color);
205
  }
206
 
207
- .copy-button:hover {
208
  background-color: var(--secondary-hover);
209
  }
210
 
@@ -302,6 +339,13 @@
302
  padding: calc(var(--spacing-unit) * 0.75) var(--spacing-unit);
303
  font-size: 0.95rem;
304
  }
 
 
 
 
 
 
 
305
  }
306
  </style>
307
  </head>
@@ -341,13 +385,14 @@
341
 
342
  <div id="upload-section" class="upload-section">
343
  <div class="upload-icon">📤</div>
344
- <p>Cliquez ou glissez-déposez une image ici</p>
345
- <input type="file" id="file-input" accept="image/*">
346
- <div class="preview-container">
347
- <img id="image-preview" src="#" alt="Aperçu de l'image">
348
  </div>
349
  </div>
350
-
 
351
  <button id="solve-button" class="button" disabled>🔍 Résoudre</button>
352
 
353
  <div id="solving-container">
@@ -368,8 +413,9 @@
368
  document.addEventListener('DOMContentLoaded', function() {
369
  const uploadSection = document.getElementById('upload-section');
370
  const fileInput = document.getElementById('file-input');
371
- const imagePreview = document.getElementById('image-preview');
372
  const solveButton = document.getElementById('solve-button');
 
373
  const solvingContainer = document.getElementById('solving-container');
374
  const responseContainer = document.getElementById('response-container');
375
  const responseDiv = document.getElementById('response');
@@ -377,7 +423,7 @@
377
  const statusElement = document.getElementById('status');
378
  const loadingText = document.getElementById('loading-text');
379
 
380
- let selectedFile = null;
381
 
382
  window.selectStyle = function(style) {
383
  document.getElementById(`style-${style}`).checked = true;
@@ -402,39 +448,105 @@
402
  uploadSection.style.backgroundColor = '#f8f9fa';
403
 
404
  if (e.dataTransfer.files.length) {
405
- handleFileSelection(e.dataTransfer.files[0]);
406
  }
407
  });
408
 
409
  fileInput.addEventListener('change', (e) => {
410
  if (e.target.files.length) {
411
- handleFileSelection(e.target.files[0]);
412
  }
413
  });
414
-
415
- function handleFileSelection(file) {
416
- if (!file.type.startsWith('image/')) {
417
- alert('Veuillez sélectionner une image valide (format PNG, JPG, GIF, etc.)');
418
- return;
419
- }
420
-
421
- selectedFile = file;
422
- solveButton.disabled = false;
423
- solveButton.textContent = '🔍 Résoudre';
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
- const reader = new FileReader();
426
- reader.onload = (e) => {
427
- imagePreview.src = e.target.result;
428
- imagePreview.style.display = 'block';
429
- };
430
- reader.readAsDataURL(file);
431
 
432
  solvingContainer.style.display = 'none';
433
  responseContainer.style.display = 'none';
434
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
  solveButton.addEventListener('click', () => {
437
- if (!selectedFile) return;
438
 
439
  const selectedStyle = document.querySelector('input[name="resolution-style"]:checked').value;
440
 
@@ -448,9 +560,16 @@
448
  responseDiv.innerHTML = '';
449
 
450
  const formData = new FormData();
451
- formData.append('image', selectedFile);
 
 
452
  formData.append('style', selectedStyle);
453
 
 
 
 
 
 
454
  fetch('/solve', {
455
  method: 'POST',
456
  body: formData
@@ -476,10 +595,14 @@
476
 
477
  if (data.error) {
478
  handleError(data.error);
 
479
  return;
480
  }
481
 
482
  updateStatus(data);
 
 
 
483
  };
484
 
485
  eventSource.onerror = function() {
@@ -505,7 +628,7 @@
505
  statusElement.textContent = 'En file d\'attente...';
506
  break;
507
  case 'processing':
508
- statusElement.innerHTML = '<span class="thinking">Mariam</span> traite votre image... <br><small>La réponse sera également envoyée sur Telegram.</small>';
509
  break;
510
  case 'completed':
511
  statusElement.className = 'status completed';
@@ -523,27 +646,37 @@
523
  function showResponse() {
524
  responseContainer.style.display = 'block';
525
  loadingText.style.display = 'none';
526
- solveButton.disabled = false;
527
- solveButton.textContent = '🔍 Résoudre';
 
 
528
  }
529
 
530
  function handleEventSourceError(taskId) {
 
531
  fetch('/task/' + taskId)
532
- .then(response => response.json())
 
 
 
 
 
533
  .then(taskData => {
534
- if (taskData.status === 'completed') {
535
  updateStatus({
536
  status: 'completed',
537
  response: taskData.response
538
  });
539
  } else if (taskData.status === 'error' || taskData.error) {
540
- handleError(taskData.error || 'Une erreur est survenue.');
541
  } else {
542
- handleError('La connexion au flux a été perdue. La réponse sera envoyée sur Telegram.');
 
543
  }
544
  })
545
- .catch(() => {
546
- handleError('La connexion au flux a été perdue et la récupération a échoué.');
 
547
  });
548
  }
549
 
@@ -587,4 +720,4 @@
587
  });
588
  </script>
589
  </body>
590
- </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Résolveur d'Images & PDF - Mariam</title>
7
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.js"></script>
8
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/contrib/auto-render.min.js"></script>
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.7/katex.min.css">
 
13
  --primary-hover: #2980b9;
14
  --secondary-color: #2ecc71;
15
  --secondary-hover: #27ae60;
16
+ --danger-color: #e74c3c;
17
+ --danger-hover: #c0392b;
18
  --background-color: #f4f7f6;
19
  --text-color: #333;
20
  --border-color: #e0e0e0;
 
166
  display: none;
167
  }
168
 
169
+ #file-preview-area {
170
  margin-top: var(--spacing-unit);
171
+ display: flex;
172
+ flex-wrap: wrap;
173
+ gap: var(--spacing-unit);
174
+ justify-content: center;
175
  }
176
 
177
+ .preview-item {
178
+ display: flex;
179
+ flex-direction: column;
180
+ align-items: center;
181
+ gap: calc(var(--spacing-unit) * 0.5);
182
+ padding: calc(var(--spacing-unit) * 0.5);
183
  border: 1px solid var(--border-color);
184
+ border-radius: 0.5rem;
185
+ background-color: #fdfdfd;
186
+ }
187
+
188
+ .preview-item img {
189
+ max-width: 100px;
190
+ max-height: 100px;
191
+ border-radius: 0.25rem;
192
+ object-fit: cover;
193
+ }
194
+ .preview-item .pdf-icon {
195
+ font-size: 3rem; /* Adjust as needed */
196
+ color: var(--danger-color);
197
+ }
198
+ .preview-item span {
199
+ font-size: 0.8rem;
200
+ color: #555;
201
+ word-break: break-all;
202
+ max-width: 100px;
203
+ text-align: center;
204
  }
205
 
206
+
207
  .button {
208
  width: 100%;
209
  padding: var(--spacing-unit);
 
227
  background-color: #bdc3c7;
228
  cursor: not-allowed;
229
  }
230
+
231
+ .clear-button {
232
+ background-color: var(--danger-color);
233
+ margin-top: 0; /* No top margin if it follows directly */
234
+ }
235
+ .clear-button:hover:not(:disabled) {
236
+ background-color: var(--danger-hover);
237
+ }
238
+
239
 
240
  .copy-button {
241
  background-color: var(--secondary-color);
242
  }
243
 
244
+ .copy-button:hover:not(:disabled) {
245
  background-color: var(--secondary-hover);
246
  }
247
 
 
339
  padding: calc(var(--spacing-unit) * 0.75) var(--spacing-unit);
340
  font-size: 0.95rem;
341
  }
342
+ .preview-item img {
343
+ max-width: 80px;
344
+ max-height: 80px;
345
+ }
346
+ .preview-item .pdf-icon {
347
+ font-size: 2.5rem;
348
+ }
349
  }
350
  </style>
351
  </head>
 
385
 
386
  <div id="upload-section" class="upload-section">
387
  <div class="upload-icon">📤</div>
388
+ <p>Cliquez ou glissez-déposez vos images et/ou 1 fichier PDF ici</p>
389
+ <input type="file" id="file-input" accept="image/*,application/pdf" multiple>
390
+ <div id="file-preview-area">
391
+ <!-- Les aperçus des fichiers seront ajoutés ici -->
392
  </div>
393
  </div>
394
+
395
+ <button id="clear-files-button" class="button clear-button" style="display: none;">🗑️ Effacer les fichiers</button>
396
  <button id="solve-button" class="button" disabled>🔍 Résoudre</button>
397
 
398
  <div id="solving-container">
 
413
  document.addEventListener('DOMContentLoaded', function() {
414
  const uploadSection = document.getElementById('upload-section');
415
  const fileInput = document.getElementById('file-input');
416
+ const filePreviewArea = document.getElementById('file-preview-area');
417
  const solveButton = document.getElementById('solve-button');
418
+ const clearFilesButton = document.getElementById('clear-files-button');
419
  const solvingContainer = document.getElementById('solving-container');
420
  const responseContainer = document.getElementById('response-container');
421
  const responseDiv = document.getElementById('response');
 
423
  const statusElement = document.getElementById('status');
424
  const loadingText = document.getElementById('loading-text');
425
 
426
+ let selectedFiles = []; // Tableau pour stocker les fichiers sélectionnés
427
 
428
  window.selectStyle = function(style) {
429
  document.getElementById(`style-${style}`).checked = true;
 
448
  uploadSection.style.backgroundColor = '#f8f9fa';
449
 
450
  if (e.dataTransfer.files.length) {
451
+ handleFileSelection(e.dataTransfer.files);
452
  }
453
  });
454
 
455
  fileInput.addEventListener('change', (e) => {
456
  if (e.target.files.length) {
457
+ handleFileSelection(e.target.files);
458
  }
459
  });
460
+
461
+ function handleFileSelection(files) {
462
+ const newFiles = Array.from(files);
463
+ let pdfAlreadySelected = selectedFiles.some(f => f.type === 'application/pdf');
464
+
465
+ newFiles.forEach(file => {
466
+ if (file.type.startsWith('image/')) {
467
+ if (!selectedFiles.some(sf => sf.name === file.name && sf.size === file.size)) { // Évite les doublons
468
+ selectedFiles.push(file);
469
+ }
470
+ } else if (file.type === 'application/pdf') {
471
+ if (!pdfAlreadySelected) {
472
+ // Supprimer tout PDF existant avant d'ajouter le nouveau
473
+ selectedFiles = selectedFiles.filter(f => f.type !== 'application/pdf');
474
+ selectedFiles.push(file);
475
+ pdfAlreadySelected = true; // Marquer qu'un PDF est maintenant sélectionné
476
+ } else {
477
+ alert('Vous ne pouvez sélectionner qu\'un seul fichier PDF.');
478
+ }
479
+ } else {
480
+ alert(`Le fichier "${file.name}" n'est pas une image ou un PDF valide et sera ignoré.`);
481
+ }
482
+ });
483
 
484
+ updateFilePreviews();
485
+ updateButtonsState();
 
 
 
 
486
 
487
  solvingContainer.style.display = 'none';
488
  responseContainer.style.display = 'none';
489
  }
490
+
491
+ function updateFilePreviews() {
492
+ filePreviewArea.innerHTML = ''; // Clear previous previews
493
+
494
+ if (selectedFiles.length === 0) {
495
+ filePreviewArea.style.display = 'none';
496
+ return;
497
+ }
498
+ filePreviewArea.style.display = 'flex';
499
+
500
+ selectedFiles.forEach(file => {
501
+ const previewItem = document.createElement('div');
502
+ previewItem.classList.add('preview-item');
503
+
504
+ const fileNameSpan = document.createElement('span');
505
+ fileNameSpan.textContent = file.name.length > 15 ? file.name.substring(0,12) + "..." : file.name;
506
+
507
+
508
+ if (file.type.startsWith('image/')) {
509
+ const img = document.createElement('img');
510
+ const reader = new FileReader();
511
+ reader.onload = (e) => {
512
+ img.src = e.target.result;
513
+ };
514
+ reader.readAsDataURL(file);
515
+ previewItem.appendChild(img);
516
+ } else if (file.type === 'application/pdf') {
517
+ const pdfIcon = document.createElement('div');
518
+ pdfIcon.classList.add('pdf-icon');
519
+ pdfIcon.textContent = '📄'; // Ou une icône SVG/FontAwesome
520
+ previewItem.appendChild(pdfIcon);
521
+ }
522
+ previewItem.appendChild(fileNameSpan);
523
+ filePreviewArea.appendChild(previewItem);
524
+ });
525
+ }
526
+
527
+ function updateButtonsState() {
528
+ if (selectedFiles.length > 0) {
529
+ solveButton.disabled = false;
530
+ solveButton.textContent = `🔍 Résoudre (${selectedFiles.length} fichier(s))`;
531
+ clearFilesButton.style.display = 'block';
532
+ } else {
533
+ solveButton.disabled = true;
534
+ solveButton.textContent = '🔍 Résoudre';
535
+ clearFilesButton.style.display = 'none';
536
+ }
537
+ }
538
+
539
+ clearFilesButton.addEventListener('click', () => {
540
+ selectedFiles = [];
541
+ fileInput.value = ''; // Important pour permettre de re-sélectionner les mêmes fichiers
542
+ updateFilePreviews();
543
+ updateButtonsState();
544
+ solvingContainer.style.display = 'none';
545
+ responseContainer.style.display = 'none';
546
+ });
547
 
548
  solveButton.addEventListener('click', () => {
549
+ if (selectedFiles.length === 0) return;
550
 
551
  const selectedStyle = document.querySelector('input[name="resolution-style"]:checked').value;
552
 
 
560
  responseDiv.innerHTML = '';
561
 
562
  const formData = new FormData();
563
+ selectedFiles.forEach(file => {
564
+ formData.append('user_files', file); // Envoyer chaque fichier sous la clé 'user_files'
565
+ });
566
  formData.append('style', selectedStyle);
567
 
568
+ // Pour le débogage : afficher le contenu de FormData
569
+ // for (let [key, value] of formData.entries()) {
570
+ // console.log(`${key}: ${value.name || value}`);
571
+ // }
572
+
573
  fetch('/solve', {
574
  method: 'POST',
575
  body: formData
 
595
 
596
  if (data.error) {
597
  handleError(data.error);
598
+ eventSource.close(); // Important de fermer en cas d'erreur finale
599
  return;
600
  }
601
 
602
  updateStatus(data);
603
+ if (data.status === 'completed' || data.status === 'error') {
604
+ eventSource.close();
605
+ }
606
  };
607
 
608
  eventSource.onerror = function() {
 
628
  statusElement.textContent = 'En file d\'attente...';
629
  break;
630
  case 'processing':
631
+ statusElement.innerHTML = '<span class="thinking">Mariam</span> traite vos fichiers... <br><small>La réponse sera également envoyée sur Telegram.</small>';
632
  break;
633
  case 'completed':
634
  statusElement.className = 'status completed';
 
646
  function showResponse() {
647
  responseContainer.style.display = 'block';
648
  loadingText.style.display = 'none';
649
+ // Ne pas réactiver le bouton ici si on veut attendre que l'utilisateur efface les fichiers
650
+ // solveButton.disabled = false;
651
+ // solveButton.textContent = `🔍 Résoudre (${selectedFiles.length} fichier(s))`;
652
+ // Laisser le bouton "Effacer les fichiers" comme principal moyen de recommencer
653
  }
654
 
655
  function handleEventSourceError(taskId) {
656
+ // Essayer de récupérer l'état final de la tâche
657
  fetch('/task/' + taskId)
658
+ .then(response => {
659
+ if (!response.ok) {
660
+ throw new Error(`Erreur serveur lors de la récupération de la tâche: ${response.status}`);
661
+ }
662
+ return response.json();
663
+ })
664
  .then(taskData => {
665
+ if (taskData.status === 'completed' && taskData.response) {
666
  updateStatus({
667
  status: 'completed',
668
  response: taskData.response
669
  });
670
  } else if (taskData.status === 'error' || taskData.error) {
671
+ handleError(taskData.error || 'Une erreur est survenue lors du traitement de la tâche.');
672
  } else {
673
+ // Si la tâche n'est pas terminée ou en erreur, mais que le flux est perdu.
674
+ handleError('La connexion au flux a été perdue. Vérifiez Telegram ou réessayez plus tard.');
675
  }
676
  })
677
+ .catch((err) => {
678
+ console.error("Erreur lors de la récupération de l'état de la tâche:", err);
679
+ handleError('La connexion au flux a été perdue et la récupération de l\'état final a échoué. La réponse pourrait être sur Telegram.');
680
  });
681
  }
682
 
 
720
  });
721
  </script>
722
  </body>
723
+ </html>