Spaces:
Sleeping
Sleeping
Update templates/index.html
Browse files- 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 |
-
|
|
|
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
|
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;
|
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 |
-
|
298 |
-
|
299 |
-
|
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 |
-
|
|
|
|
|
|
|
310 |
resetUI();
|
311 |
-
// Use SweetAlert for this message too
|
312 |
Swal.fire({
|
313 |
icon: 'warning',
|
314 |
-
title: 'Type de fichier
|
315 |
-
text: '
|
316 |
});
|
317 |
}
|
318 |
}
|
@@ -321,7 +316,21 @@
|
|
321 |
|
322 |
// --- Event Listeners ---
|
323 |
fileTypeRadios.forEach(radio => {
|
324 |
-
radio.addEventListener('change',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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({
|
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 |
-
|
|
|
371 |
const isImageTypeByName = /\.(jpe?g|png|gif|webp)$/i.test(file.name);
|
372 |
if (!isImageTypeByName) {
|
373 |
-
Swal.fire({
|
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 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
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 |
-
|
|
|
|
|
|
|
430 |
const formData = new FormData();
|
431 |
formData.append('file', selectedFile);
|
432 |
-
formData.append('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;
|
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')
|
507 |
-
.replace(/<[^>]+>/g, '')
|
508 |
-
.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&')
|
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,
|
519 |
-
showConfirmButton: false
|
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>
|