Docfile commited on
Commit
063d916
·
verified ·
1 Parent(s): 3f90d17

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +62 -81
templates/index.html CHANGED
@@ -136,10 +136,11 @@
136
  <span class="text-sm text-gray-700 group-hover:text-indigo-600">Document iconographique (Image, Photo, Dessin...) avec méthodologie.</span>
137
  </label>
138
  <label class="relative flex items-center group cursor-pointer p-3 border border-gray-200 rounded-lg hover:bg-indigo-50 transition-colors w-full">
139
- <input type="radio" name="fileType" value="text"
140
  class="peer sr-only file-type-radio">
141
  <div class="w-5 h-5 mr-3 border-2 border-gray-300 rounded-full flex-shrink-0 peer-checked:border-indigo-500 peer-checked:bg-indigo-500 transition-all"></div>
142
- <span class="text-sm text-gray-700 group-hover:text-indigo-600">Document textuel (Texte, PDF lisible...) avec méthodologie.</span>
 
143
  </label>
144
  </div>
145
  </div>
@@ -148,7 +149,7 @@
148
  <div class="mb-6">
149
  <label class="block text-base font-medium text-gray-700 mb-3">2. Sélectionnez votre fichier</label>
150
  <div id="upload-zone" class="upload-zone border-2 border-dashed border-gray-300 rounded-xl p-6 text-center cursor-pointer transition-all duration-300 relative overflow-hidden">
151
- <input id="file-upload" name="file" type="file" class="sr-only" accept="image/*">
152
  <div class="space-y-3">
153
  <div class="w-16 h-16 mx-auto rounded-full bg-indigo-100 flex items-center justify-center border-4 border-white shadow-sm">
154
  <svg class="w-8 h-8 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -162,7 +163,7 @@
162
  <span class="text-gray-500"> ou </span>
163
  <label for="file-upload" class="cursor-pointer text-indigo-600 hover:text-indigo-500 font-medium">cliquez pour sélectionner</label>
164
  </div>
165
- <p class="text-xs text-gray-500" id="file-types-info">Images (PNG, JPG, GIF) ou Textes (TXT, PDF) - Max 16MB</p>
166
  </div>
167
  </div>
168
  </div>
@@ -172,7 +173,7 @@
172
  <h3 class="text-base font-medium text-gray-800 mb-3">Aperçu</h3>
173
  <div class="preview-container border rounded-lg p-4 bg-gray-50 max-h-72 overflow-auto relative">
174
  <img id="image-preview" class="max-w-full h-auto hidden rounded block mx-auto" alt="Aperçu Image">
175
- <pre id="text-preview" class="text-sm text-gray-700 whitespace-pre-wrap hidden bg-white p-3 rounded shadow-inner"></pre>
176
  <p id="file-info" class="text-xs text-gray-500 mt-2 text-center"></p>
177
  <button id="remove-file-button" class="absolute top-2 right-2 bg-red-100 text-red-600 hover:bg-red-200 rounded-full p-1 text-xs hidden" title="Retirer le fichier">
178
  <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
@@ -227,7 +228,7 @@
227
  const fileInput = document.getElementById('file-upload');
228
  const preview = document.getElementById('preview');
229
  const imagePreview = document.getElementById('image-preview');
230
- const textPreview = document.getElementById('text-preview');
231
  const fileInfo = document.getElementById('file-info');
232
  const removeFileButton = document.getElementById('remove-file-button');
233
  const submitButton = document.getElementById('submit-button');
@@ -240,6 +241,8 @@
240
  const fileTypesInfo = document.getElementById('file-types-info');
241
 
242
  let selectedFile = null;
 
 
243
 
244
  // Configure marked with Prism check
245
  marked.setOptions({
@@ -248,16 +251,14 @@
248
  headerIds: false,
249
  mangle: false,
250
  highlight: function(code, lang) {
251
- // ** Check if Prism and the language are loaded **
252
  if (typeof Prism !== 'undefined' && Prism.languages[lang]) {
253
  try {
254
  return Prism.highlight(code, Prism.languages[lang], lang);
255
  } catch (error) {
256
  console.warn(`Prism highlighting failed for language ${lang}:`, error)
257
- return code; // Return unhighlighted on error
258
  }
259
  }
260
- // ** Fallback if Prism or language not available **
261
  return code;
262
  }
263
  });
@@ -289,30 +290,24 @@
289
  analysisContent.innerHTML = '<p class="text-gray-500">L\'analyse apparaîtra ici...</p>';
290
  submitButton.disabled = true;
291
  showLoading(false);
292
- updateAcceptAttribute();
293
  }
294
 
295
  function updateAcceptAttribute() {
296
  const selectedType = document.querySelector('input[name="fileType"]:checked').value;
297
- if (selectedType === 'image') {
298
- fileInput.accept = "image/png, image/jpeg, image/gif, image/webp";
299
- fileTypesInfo.textContent = "Images (PNG, JPG, GIF, WEBP) - Max 16MB";
300
- } else {
301
- fileInput.accept = "text/plain, application/pdf, .txt";
302
- fileTypesInfo.textContent = "Textes (TXT, PDF lisible) - Max 16MB";
303
- }
304
- if (selectedFile) {
305
- const currentFileTypeMatchesSelection =
306
- (selectedType === 'image' && selectedFile.type.startsWith('image/')) ||
307
- (selectedType === 'text' && (selectedFile.type === 'text/plain' || selectedFile.type === 'application/pdf' || selectedFile.name.endsWith('.txt')));
308
 
309
- if(!currentFileTypeMatchesSelection) {
 
 
 
310
  resetUI();
311
- // Use SweetAlert for this message too
312
  Swal.fire({
313
  icon: 'warning',
314
- title: 'Type de fichier changé',
315
- text: 'Le type de document a été modifié. Veuillez resélectionner votre fichier.',
316
  });
317
  }
318
  }
@@ -321,7 +316,21 @@
321
 
322
  // --- Event Listeners ---
323
  fileTypeRadios.forEach(radio => {
324
- radio.addEventListener('change', updateAcceptAttribute);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  });
326
  uploadZone.addEventListener('click', () => fileInput.click());
327
  uploadZone.addEventListener('dragover', (e) => {
@@ -351,11 +360,11 @@
351
  // --- Core Logic Functions ---
352
 
353
  function handleFileSelection(file) {
354
- const fileType = document.querySelector('input[name="fileType"]:checked').value;
355
  const maxFileSize = 16 * 1024 * 1024;
356
 
357
  if (file.size > maxFileSize) {
358
- Swal.fire({ // Use SweetAlert
359
  icon: 'error',
360
  title: 'Fichier trop volumineux',
361
  text: `La taille maximale autorisée est de 16 Mo. Votre fichier fait ${(file.size / 1024 / 1024).toFixed(1)} Mo.`,
@@ -365,12 +374,12 @@
365
  }
366
 
367
  const allowedImageTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
368
- const allowedTextTypes = ['text/plain', 'application/pdf'];
369
 
370
- if (fileType === 'image' && !allowedImageTypes.includes(file.type)) {
 
371
  const isImageTypeByName = /\.(jpe?g|png|gif|webp)$/i.test(file.name);
372
  if (!isImageTypeByName) {
373
- Swal.fire({ // Use SweetAlert
374
  icon: 'error',
375
  title: 'Type de fichier invalide',
376
  text: 'Veuillez sélectionner une image (PNG, JPG, GIF, WEBP).'
@@ -378,20 +387,8 @@
378
  resetUI();
379
  return;
380
  }
381
- } else if (fileType === 'text' && !allowedTextTypes.includes(file.type)) {
382
- const isTextTypeByName = /\.txt$/i.test(file.name);
383
- if (!isTextTypeByName && file.type !== 'application/pdf') {
384
- Swal.fire({ // Use SweetAlert
385
- icon: 'error',
386
- title: 'Type de fichier invalide',
387
- text: 'Veuillez sélectionner un fichier texte (TXT) ou PDF.'
388
- });
389
- resetUI();
390
- return;
391
- }
392
  }
393
 
394
- // If validation passes:
395
  selectedFile = file;
396
  preview.classList.remove('hidden');
397
  preview.classList.add('fade-in');
@@ -400,36 +397,30 @@
400
  fileInfo.textContent = `${file.name} (${(file.size / 1024).toFixed(1)} Ko)`;
401
  removeFileButton.classList.remove('hidden');
402
 
403
- if (fileType === 'image') {
404
- imagePreview.classList.remove('hidden');
405
- textPreview.classList.add('hidden');
406
- const reader = new FileReader();
407
- reader.onload = (e) => imagePreview.src = e.target.result;
408
- reader.readAsDataURL(file);
409
- } else {
410
- imagePreview.classList.add('hidden');
411
- textPreview.classList.remove('hidden');
412
- if (file.type === 'application/pdf') {
413
- textPreview.textContent = "Aperçu non disponible pour les fichiers PDF. Le contenu sera analysé.";
414
- } else {
415
- const reader = new FileReader();
416
- reader.onload = (e) => textPreview.textContent = e.target.result;
417
- reader.readAsText(file);
418
- }
419
- }
420
  submitButton.disabled = false;
421
  }
422
 
423
  function handleSubmit() {
424
  if (!selectedFile) {
425
- Swal.fire('Aucun fichier', 'Veuillez sélectionner un fichier avant de lancer l\'analyse.', 'warning');
426
  return;
427
  }
428
 
429
- const fileType = document.querySelector('input[name="fileType"]:checked').value;
 
 
 
430
  const formData = new FormData();
431
  formData.append('file', selectedFile);
432
- formData.append('fileType', fileType);
433
 
434
  showLoading(true);
435
  results.classList.add('hidden');
@@ -455,11 +446,7 @@
455
  results.classList.add('slide-up');
456
  analysisContent.innerHTML = marked.parse(data.analysis);
457
 
458
- // ** Check if Prism is loaded before highlighting **
459
  if (typeof Prism !== 'undefined') {
460
- // Use Prism's autoloader or highlightAllUnder
461
- // Prism.highlightAllUnder(analysisContent); // Might work with autoloader
462
- // Or manually trigger highlighting on specific elements if autoloader fails:
463
  analysisContent.querySelectorAll('pre code').forEach((block) => {
464
  Prism.highlightElement(block);
465
  });
@@ -474,15 +461,13 @@
474
  })
475
  .catch(error => {
476
  showLoading(false);
477
- // Use SweetAlert for error display
478
  Swal.fire({
479
  icon: 'error',
480
  title: 'Erreur lors de l\'analyse',
481
  text: error.message || 'Une erreur inconnue est survenue.',
482
  });
483
- // Optionally log detailed error
484
  console.error('Error during analysis:', error);
485
- submitButton.disabled = false; // Re-enable on error
486
  });
487
  }
488
 
@@ -497,31 +482,28 @@
497
  tempDiv.innerHTML = analysisHtml;
498
  let textToCopy = tempDiv.textContent || tempDiv.innerText || "";
499
 
500
- // Basic conversion for better text format
501
  textToCopy = analysisHtml
502
  .replace(/<br\s*\/?>/gi, '\n')
503
  .replace(/<\/p>/gi, '\n')
504
  .replace(/<\/li>/gi, '\n')
505
  .replace(/<\/(h[1-6])>/gi, '\n\n')
506
- .replace(/<pre.*?><code.*?>([\s\S]*?)<\/code><\/pre>/gi, '\n```\n$1\n```\n') // Attempt to format code blocks
507
- .replace(/<[^>]+>/g, '') // Remove remaining tags
508
- .replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&') // Decode entities
509
  .trim();
510
 
511
  navigator.clipboard.writeText(textToCopy)
512
  .then(() => {
513
- // Use SweetAlert for success
514
  Swal.fire({
515
  icon: 'success',
516
  title: 'Copié !',
517
  text: 'L\'analyse a été copiée dans le presse-papiers.',
518
- timer: 1500, // Auto-close after 1.5 seconds
519
- showConfirmButton: false // Hide the confirmation button
520
  });
521
  })
522
  .catch(err => {
523
  console.error('Erreur de copie: ', err);
524
- // Use SweetAlert for error
525
  Swal.fire({
526
  icon: 'error',
527
  title: 'Erreur de copie',
@@ -530,10 +512,9 @@
530
  });
531
  }
532
 
533
-
534
  // --- Initial Setup ---
535
- updateAcceptAttribute();
536
- resetUI();
537
 
538
  }); // End DOMContentLoaded
539
  </script>
 
136
  <span class="text-sm text-gray-700 group-hover:text-indigo-600">Document iconographique (Image, Photo, Dessin...) avec méthodologie.</span>
137
  </label>
138
  <label class="relative flex items-center group cursor-pointer p-3 border border-gray-200 rounded-lg hover:bg-indigo-50 transition-colors w-full">
139
+ <input type="radio" name="fileType" value="text_image" <!-- MODIFIED: value to distinguish if needed, or keep "text" if backend logic depends on it -->
140
  class="peer sr-only file-type-radio">
141
  <div class="w-5 h-5 mr-3 border-2 border-gray-300 rounded-full flex-shrink-0 peer-checked:border-indigo-500 peer-checked:bg-indigo-500 transition-all"></div>
142
+ <!-- MODIFIED LABEL -->
143
+ <span class="text-sm text-gray-700 group-hover:text-indigo-600">Image d'un document textuel (ex: capture d'écran de texte) avec méthodologie.</span>
144
  </label>
145
  </div>
146
  </div>
 
149
  <div class="mb-6">
150
  <label class="block text-base font-medium text-gray-700 mb-3">2. Sélectionnez votre fichier</label>
151
  <div id="upload-zone" class="upload-zone border-2 border-dashed border-gray-300 rounded-xl p-6 text-center cursor-pointer transition-all duration-300 relative overflow-hidden">
152
+ <input id="file-upload" name="file" type="file" class="sr-only" accept="image/*"> <!-- Default accept to image/* -->
153
  <div class="space-y-3">
154
  <div class="w-16 h-16 mx-auto rounded-full bg-indigo-100 flex items-center justify-center border-4 border-white shadow-sm">
155
  <svg class="w-8 h-8 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 
163
  <span class="text-gray-500"> ou </span>
164
  <label for="file-upload" class="cursor-pointer text-indigo-600 hover:text-indigo-500 font-medium">cliquez pour sélectionner</label>
165
  </div>
166
+ <p class="text-xs text-gray-500" id="file-types-info">Images (PNG, JPG, GIF, WEBP) - Max 16MB</p> <!-- Default info text -->
167
  </div>
168
  </div>
169
  </div>
 
173
  <h3 class="text-base font-medium text-gray-800 mb-3">Aperçu</h3>
174
  <div class="preview-container border rounded-lg p-4 bg-gray-50 max-h-72 overflow-auto relative">
175
  <img id="image-preview" class="max-w-full h-auto hidden rounded block mx-auto" alt="Aperçu Image">
176
+ <pre id="text-preview" class="text-sm text-gray-700 whitespace-pre-wrap hidden bg-white p-3 rounded shadow-inner"></pre> <!-- Kept for potential future use, but will be hidden for text_image -->
177
  <p id="file-info" class="text-xs text-gray-500 mt-2 text-center"></p>
178
  <button id="remove-file-button" class="absolute top-2 right-2 bg-red-100 text-red-600 hover:bg-red-200 rounded-full p-1 text-xs hidden" title="Retirer le fichier">
179
  <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
 
228
  const fileInput = document.getElementById('file-upload');
229
  const preview = document.getElementById('preview');
230
  const imagePreview = document.getElementById('image-preview');
231
+ const textPreview = document.getElementById('text-preview'); // Still used for clearing
232
  const fileInfo = document.getElementById('file-info');
233
  const removeFileButton = document.getElementById('remove-file-button');
234
  const submitButton = document.getElementById('submit-button');
 
241
  const fileTypesInfo = document.getElementById('file-types-info');
242
 
243
  let selectedFile = null;
244
+ const imageMimeTypes = "image/png, image/jpeg, image/gif, image/webp";
245
+ const imageInfoText = "Images (PNG, JPG, GIF, WEBP) - Max 16MB";
246
 
247
  // Configure marked with Prism check
248
  marked.setOptions({
 
251
  headerIds: false,
252
  mangle: false,
253
  highlight: function(code, lang) {
 
254
  if (typeof Prism !== 'undefined' && Prism.languages[lang]) {
255
  try {
256
  return Prism.highlight(code, Prism.languages[lang], lang);
257
  } catch (error) {
258
  console.warn(`Prism highlighting failed for language ${lang}:`, error)
259
+ return code;
260
  }
261
  }
 
262
  return code;
263
  }
264
  });
 
290
  analysisContent.innerHTML = '<p class="text-gray-500">L\'analyse apparaîtra ici...</p>';
291
  submitButton.disabled = true;
292
  showLoading(false);
293
+ updateAcceptAttribute(); // Ensure accept attribute is correct on reset
294
  }
295
 
296
  function updateAcceptAttribute() {
297
  const selectedType = document.querySelector('input[name="fileType"]:checked').value;
298
+ // MODIFICATION: Both options now accept images
299
+ fileInput.accept = imageMimeTypes;
300
+ fileTypesInfo.textContent = imageInfoText;
 
 
 
 
 
 
 
 
301
 
302
+ if (selectedFile) {
303
+ // Now, both types expect an image. If a file was selected, it must be an image.
304
+ const isImage = selectedFile.type.startsWith('image/');
305
+ if(!isImage) {
306
  resetUI();
 
307
  Swal.fire({
308
  icon: 'warning',
309
+ title: 'Type de fichier incompatible',
310
+ text: 'Seuls les fichiers images sont acceptés. Veuillez resélectionner votre fichier.',
311
  });
312
  }
313
  }
 
316
 
317
  // --- Event Listeners ---
318
  fileTypeRadios.forEach(radio => {
319
+ radio.addEventListener('change', () => {
320
+ // No need to call updateAcceptAttribute if it always accepts images,
321
+ // but if you changed the radio value to be different, you might re-evaluate.
322
+ // For now, we'll just check if a file is selected and if it's still valid (image)
323
+ // Or, more simply, just reset if type changes and a file is loaded, to force re-selection
324
+ if (selectedFile) {
325
+ resetUI();
326
+ Swal.fire({
327
+ icon: 'info',
328
+ title: 'Type de document changé',
329
+ text: 'Le type de méthodologie a été modifié. Veuillez resélectionner votre fichier image.',
330
+ });
331
+ }
332
+ updateAcceptAttribute(); // Still useful to call for consistency, though its effect is now static on accept types
333
+ });
334
  });
335
  uploadZone.addEventListener('click', () => fileInput.click());
336
  uploadZone.addEventListener('dragover', (e) => {
 
360
  // --- Core Logic Functions ---
361
 
362
  function handleFileSelection(file) {
363
+ // const fileType = document.querySelector('input[name="fileType"]:checked').value; // fileType radio value
364
  const maxFileSize = 16 * 1024 * 1024;
365
 
366
  if (file.size > maxFileSize) {
367
+ Swal.fire({
368
  icon: 'error',
369
  title: 'Fichier trop volumineux',
370
  text: `La taille maximale autorisée est de 16 Mo. Votre fichier fait ${(file.size / 1024 / 1024).toFixed(1)} Mo.`,
 
374
  }
375
 
376
  const allowedImageTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
 
377
 
378
+ // MODIFICATION: Always validate as image
379
+ if (!allowedImageTypes.includes(file.type)) {
380
  const isImageTypeByName = /\.(jpe?g|png|gif|webp)$/i.test(file.name);
381
  if (!isImageTypeByName) {
382
+ Swal.fire({
383
  icon: 'error',
384
  title: 'Type de fichier invalide',
385
  text: 'Veuillez sélectionner une image (PNG, JPG, GIF, WEBP).'
 
387
  resetUI();
388
  return;
389
  }
 
 
 
 
 
 
 
 
 
 
 
390
  }
391
 
 
392
  selectedFile = file;
393
  preview.classList.remove('hidden');
394
  preview.classList.add('fade-in');
 
397
  fileInfo.textContent = `${file.name} (${(file.size / 1024).toFixed(1)} Ko)`;
398
  removeFileButton.classList.remove('hidden');
399
 
400
+ // MODIFICATION: Always show image preview
401
+ imagePreview.classList.remove('hidden');
402
+ textPreview.classList.add('hidden'); // Ensure text preview is hidden
403
+ textPreview.textContent = ''; // Clear any old text preview content
404
+ const reader = new FileReader();
405
+ reader.onload = (e) => imagePreview.src = e.target.result;
406
+ reader.readAsDataURL(file);
407
+
 
 
 
 
 
 
 
 
 
408
  submitButton.disabled = false;
409
  }
410
 
411
  function handleSubmit() {
412
  if (!selectedFile) {
413
+ Swal.fire('Aucun fichier', 'Veuillez sélectionner un fichier image avant de lancer l\'analyse.', 'warning');
414
  return;
415
  }
416
 
417
+ // This 'fileType' value ('image' or 'text_image') will be sent to the backend.
418
+ // The backend can use this to apply different analysis methodologies
419
+ // even though the uploaded file is always an image.
420
+ const fileTypeForBackend = document.querySelector('input[name="fileType"]:checked').value;
421
  const formData = new FormData();
422
  formData.append('file', selectedFile);
423
+ formData.append('fileType', fileTypeForBackend); // Send the selected radio value
424
 
425
  showLoading(true);
426
  results.classList.add('hidden');
 
446
  results.classList.add('slide-up');
447
  analysisContent.innerHTML = marked.parse(data.analysis);
448
 
 
449
  if (typeof Prism !== 'undefined') {
 
 
 
450
  analysisContent.querySelectorAll('pre code').forEach((block) => {
451
  Prism.highlightElement(block);
452
  });
 
461
  })
462
  .catch(error => {
463
  showLoading(false);
 
464
  Swal.fire({
465
  icon: 'error',
466
  title: 'Erreur lors de l\'analyse',
467
  text: error.message || 'Une erreur inconnue est survenue.',
468
  });
 
469
  console.error('Error during analysis:', error);
470
+ submitButton.disabled = false;
471
  });
472
  }
473
 
 
482
  tempDiv.innerHTML = analysisHtml;
483
  let textToCopy = tempDiv.textContent || tempDiv.innerText || "";
484
 
 
485
  textToCopy = analysisHtml
486
  .replace(/<br\s*\/?>/gi, '\n')
487
  .replace(/<\/p>/gi, '\n')
488
  .replace(/<\/li>/gi, '\n')
489
  .replace(/<\/(h[1-6])>/gi, '\n\n')
490
+ .replace(/<pre.*?><code.*?>([\s\S]*?)<\/code><\/pre>/gi, '\n```\n$1\n```\n')
491
+ .replace(/<[^>]+>/g, '')
492
+ .replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&')
493
  .trim();
494
 
495
  navigator.clipboard.writeText(textToCopy)
496
  .then(() => {
 
497
  Swal.fire({
498
  icon: 'success',
499
  title: 'Copié !',
500
  text: 'L\'analyse a été copiée dans le presse-papiers.',
501
+ timer: 1500,
502
+ showConfirmButton: false
503
  });
504
  })
505
  .catch(err => {
506
  console.error('Erreur de copie: ', err);
 
507
  Swal.fire({
508
  icon: 'error',
509
  title: 'Erreur de copie',
 
512
  });
513
  }
514
 
 
515
  // --- Initial Setup ---
516
+ updateAcceptAttribute(); // Call once to set initial state
517
+ resetUI(); // Call resetUI which also calls updateAcceptAttribute
518
 
519
  }); // End DOMContentLoaded
520
  </script>