Spaces:
Sleeping
Sleeping
Update templates/index.html
Browse files- templates/index.html +44 -60
templates/index.html
CHANGED
@@ -136,11 +136,10 @@
|
|
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="
|
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 |
-
<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,7 +148,7 @@
|
|
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/*">
|
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,7 +162,7 @@
|
|
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>
|
167 |
</div>
|
168 |
</div>
|
169 |
</div>
|
@@ -173,7 +172,7 @@
|
|
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>
|
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,7 +227,7 @@
|
|
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
|
232 |
const fileInfo = document.getElementById('file-info');
|
233 |
const removeFileButton = document.getElementById('remove-file-button');
|
234 |
const submitButton = document.getElementById('submit-button');
|
@@ -241,10 +240,7 @@
|
|
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({
|
249 |
breaks: true,
|
250 |
gfm: true,
|
@@ -256,14 +252,13 @@
|
|
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 |
});
|
265 |
|
266 |
-
// --- Helper Functions ---
|
267 |
function showLoading(isLoading) {
|
268 |
if (isLoading) {
|
269 |
submitButtonText.classList.add('hidden');
|
@@ -281,57 +276,49 @@
|
|
281 |
fileInput.value = '';
|
282 |
preview.classList.add('hidden');
|
283 |
imagePreview.classList.add('hidden');
|
284 |
-
textPreview.classList.add('hidden');
|
285 |
imagePreview.src = '#';
|
286 |
-
textPreview.textContent = '';
|
287 |
fileInfo.textContent = '';
|
288 |
removeFileButton.classList.add('hidden');
|
289 |
results.classList.add('hidden');
|
290 |
analysisContent.innerHTML = '<p class="text-gray-500">L\'analyse apparaîtra ici...</p>';
|
291 |
submitButton.disabled = true;
|
292 |
showLoading(false);
|
293 |
-
updateAcceptAttribute(); //
|
294 |
}
|
295 |
|
296 |
function updateAcceptAttribute() {
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
fileTypesInfo.textContent = imageInfoText;
|
301 |
|
302 |
if (selectedFile) {
|
303 |
-
//
|
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
|
311 |
});
|
312 |
}
|
313 |
}
|
314 |
}
|
315 |
|
316 |
-
|
317 |
-
// --- Event Listeners ---
|
318 |
fileTypeRadios.forEach(radio => {
|
319 |
radio.addEventListener('change', () => {
|
320 |
-
//
|
321 |
-
|
322 |
-
//
|
323 |
-
//
|
324 |
-
if
|
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) => {
|
337 |
e.preventDefault();
|
@@ -357,10 +344,8 @@
|
|
357 |
copyAnalysisButton.addEventListener('click', copyAnalysisToClipboard);
|
358 |
|
359 |
|
360 |
-
// --- Core Logic Functions ---
|
361 |
-
|
362 |
function handleFileSelection(file) {
|
363 |
-
// const
|
364 |
const maxFileSize = 16 * 1024 * 1024;
|
365 |
|
366 |
if (file.size > maxFileSize) {
|
@@ -375,11 +360,11 @@
|
|
375 |
|
376 |
const allowedImageTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
|
377 |
|
378 |
-
//
|
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).'
|
@@ -389,6 +374,7 @@
|
|
389 |
}
|
390 |
}
|
391 |
|
|
|
392 |
selectedFile = file;
|
393 |
preview.classList.remove('hidden');
|
394 |
preview.classList.add('fade-in');
|
@@ -397,30 +383,29 @@
|
|
397 |
fileInfo.textContent = `${file.name} (${(file.size / 1024).toFixed(1)} Ko)`;
|
398 |
removeFileButton.classList.remove('hidden');
|
399 |
|
400 |
-
//
|
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
|
414 |
return;
|
415 |
}
|
416 |
|
417 |
-
// This '
|
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); //
|
424 |
|
425 |
showLoading(true);
|
426 |
results.classList.add('hidden');
|
@@ -467,7 +452,8 @@
|
|
467 |
text: error.message || 'Une erreur inconnue est survenue.',
|
468 |
});
|
469 |
console.error('Error during analysis:', error);
|
470 |
-
|
|
|
471 |
});
|
472 |
}
|
473 |
|
@@ -487,9 +473,9 @@
|
|
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)
|
@@ -498,8 +484,8 @@
|
|
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 => {
|
@@ -512,9 +498,7 @@
|
|
512 |
});
|
513 |
}
|
514 |
|
515 |
-
//
|
516 |
-
updateAcceptAttribute(); // Call once to set initial state
|
517 |
-
resetUI(); // Call resetUI which also calls updateAcceptAttribute
|
518 |
|
519 |
}); // End DOMContentLoaded
|
520 |
</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"
|
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">Analyse de texte à partir d'une image (Image, Photo de texte...) avec méthodologie.</span>
|
|
|
143 |
</label>
|
144 |
</div>
|
145 |
</div>
|
|
|
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 |
<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, WEBP) - Max 16MB</p>
|
166 |
</div>
|
167 |
</div>
|
168 |
</div>
|
|
|
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 |
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'); // Still keep the element for completeness
|
231 |
const fileInfo = document.getElementById('file-info');
|
232 |
const removeFileButton = document.getElementById('remove-file-button');
|
233 |
const submitButton = document.getElementById('submit-button');
|
|
|
240 |
const fileTypesInfo = document.getElementById('file-types-info');
|
241 |
|
242 |
let selectedFile = null;
|
|
|
|
|
243 |
|
|
|
244 |
marked.setOptions({
|
245 |
breaks: true,
|
246 |
gfm: true,
|
|
|
252 |
return Prism.highlight(code, Prism.languages[lang], lang);
|
253 |
} catch (error) {
|
254 |
console.warn(`Prism highlighting failed for language ${lang}:`, error)
|
255 |
+
return code;
|
256 |
}
|
257 |
}
|
258 |
return code;
|
259 |
}
|
260 |
});
|
261 |
|
|
|
262 |
function showLoading(isLoading) {
|
263 |
if (isLoading) {
|
264 |
submitButtonText.classList.add('hidden');
|
|
|
276 |
fileInput.value = '';
|
277 |
preview.classList.add('hidden');
|
278 |
imagePreview.classList.add('hidden');
|
279 |
+
textPreview.classList.add('hidden'); // Ensure text preview is hidden
|
280 |
imagePreview.src = '#';
|
281 |
+
textPreview.textContent = ''; // Clear text preview content
|
282 |
fileInfo.textContent = '';
|
283 |
removeFileButton.classList.add('hidden');
|
284 |
results.classList.add('hidden');
|
285 |
analysisContent.innerHTML = '<p class="text-gray-500">L\'analyse apparaîtra ici...</p>';
|
286 |
submitButton.disabled = true;
|
287 |
showLoading(false);
|
288 |
+
updateAcceptAttribute(); // Call this to set initial accept types
|
289 |
}
|
290 |
|
291 |
function updateAcceptAttribute() {
|
292 |
+
// Both options now accept images
|
293 |
+
fileInput.accept = "image/png, image/jpeg, image/gif, image/webp";
|
294 |
+
fileTypesInfo.textContent = "Images (PNG, JPG, GIF, WEBP) - Max 16MB";
|
|
|
295 |
|
296 |
if (selectedFile) {
|
297 |
+
// Check if the currently selected file is an image
|
298 |
const isImage = selectedFile.type.startsWith('image/');
|
299 |
+
if (!isImage) {
|
300 |
+
// If a non-image file was somehow selected (e.g., before this logic was in place, or if user bypasses JS)
|
301 |
+
// and then the radio changes, reset.
|
302 |
resetUI();
|
303 |
Swal.fire({
|
304 |
icon: 'warning',
|
305 |
title: 'Type de fichier incompatible',
|
306 |
+
text: 'Seuls les fichiers image (PNG, JPG, GIF, WEBP) sont acceptés. Veuillez resélectionner votre fichier.',
|
307 |
});
|
308 |
}
|
309 |
}
|
310 |
}
|
311 |
|
|
|
|
|
312 |
fileTypeRadios.forEach(radio => {
|
313 |
radio.addEventListener('change', () => {
|
314 |
+
// Update accept attribute (though it's always images now)
|
315 |
+
updateAcceptAttribute();
|
316 |
+
// If a file is selected, and it's not an image (which shouldn't happen with current validation),
|
317 |
+
// the updateAcceptAttribute will handle resetting.
|
318 |
+
// Otherwise, no need to clear selection if it's already an image.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
});
|
320 |
});
|
321 |
+
|
322 |
uploadZone.addEventListener('click', () => fileInput.click());
|
323 |
uploadZone.addEventListener('dragover', (e) => {
|
324 |
e.preventDefault();
|
|
|
344 |
copyAnalysisButton.addEventListener('click', copyAnalysisToClipboard);
|
345 |
|
346 |
|
|
|
|
|
347 |
function handleFileSelection(file) {
|
348 |
+
// const intendedAnalysisType = document.querySelector('input[name="fileType"]:checked').value; // For future use if needed
|
349 |
const maxFileSize = 16 * 1024 * 1024;
|
350 |
|
351 |
if (file.size > maxFileSize) {
|
|
|
360 |
|
361 |
const allowedImageTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
|
362 |
|
363 |
+
// Both radio options now expect an image
|
364 |
if (!allowedImageTypes.includes(file.type)) {
|
365 |
const isImageTypeByName = /\.(jpe?g|png|gif|webp)$/i.test(file.name);
|
366 |
+
if (!isImageTypeByName) { // Check by extension as a fallback
|
367 |
+
Swal.fire({
|
368 |
icon: 'error',
|
369 |
title: 'Type de fichier invalide',
|
370 |
text: 'Veuillez sélectionner une image (PNG, JPG, GIF, WEBP).'
|
|
|
374 |
}
|
375 |
}
|
376 |
|
377 |
+
// If validation passes:
|
378 |
selectedFile = file;
|
379 |
preview.classList.remove('hidden');
|
380 |
preview.classList.add('fade-in');
|
|
|
383 |
fileInfo.textContent = `${file.name} (${(file.size / 1024).toFixed(1)} Ko)`;
|
384 |
removeFileButton.classList.remove('hidden');
|
385 |
|
386 |
+
// Always show image preview
|
387 |
imagePreview.classList.remove('hidden');
|
388 |
textPreview.classList.add('hidden'); // Ensure text preview is hidden
|
389 |
textPreview.textContent = ''; // Clear any old text preview content
|
390 |
+
|
391 |
const reader = new FileReader();
|
392 |
reader.onload = (e) => imagePreview.src = e.target.result;
|
393 |
reader.readAsDataURL(file);
|
394 |
+
|
395 |
submitButton.disabled = false;
|
396 |
}
|
397 |
|
398 |
function handleSubmit() {
|
399 |
if (!selectedFile) {
|
400 |
+
Swal.fire('Aucun fichier', 'Veuillez sélectionner un fichier avant de lancer l\'analyse.', 'warning');
|
401 |
return;
|
402 |
}
|
403 |
|
404 |
+
// This 'fileTypeForBackend' is crucial for the backend
|
|
|
|
|
405 |
const fileTypeForBackend = document.querySelector('input[name="fileType"]:checked').value;
|
406 |
const formData = new FormData();
|
407 |
+
formData.append('file', selectedFile); // This will always be an image file
|
408 |
+
formData.append('fileType', fileTypeForBackend); // This will be 'image' or 'text' based on radio
|
409 |
|
410 |
showLoading(true);
|
411 |
results.classList.add('hidden');
|
|
|
452 |
text: error.message || 'Une erreur inconnue est survenue.',
|
453 |
});
|
454 |
console.error('Error during analysis:', error);
|
455 |
+
// Do not re-enable button if there was a selected file, let user reset or try again with same file
|
456 |
+
// submitButton.disabled = (selectedFile === null);
|
457 |
});
|
458 |
}
|
459 |
|
|
|
473 |
.replace(/<\/p>/gi, '\n')
|
474 |
.replace(/<\/li>/gi, '\n')
|
475 |
.replace(/<\/(h[1-6])>/gi, '\n\n')
|
476 |
+
.replace(/<pre.*?><code.*?>([\s\S]*?)<\/code><\/pre>/gi, '\n```\n$1\n```\n')
|
477 |
+
.replace(/<[^>]+>/g, '')
|
478 |
+
.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&')
|
479 |
.trim();
|
480 |
|
481 |
navigator.clipboard.writeText(textToCopy)
|
|
|
484 |
icon: 'success',
|
485 |
title: 'Copié !',
|
486 |
text: 'L\'analyse a été copiée dans le presse-papiers.',
|
487 |
+
timer: 1500,
|
488 |
+
showConfirmButton: false
|
489 |
});
|
490 |
})
|
491 |
.catch(err => {
|
|
|
498 |
});
|
499 |
}
|
500 |
|
501 |
+
resetUI(); // Initial setup call
|
|
|
|
|
502 |
|
503 |
}); // End DOMContentLoaded
|
504 |
</script>
|