Update templates/index.html
Browse files- templates/index.html +94 -30
templates/index.html
CHANGED
|
@@ -192,7 +192,7 @@
|
|
| 192 |
object-fit: cover;
|
| 193 |
}
|
| 194 |
.preview-item .pdf-icon {
|
| 195 |
-
font-size: 3rem;
|
| 196 |
color: var(--danger-color);
|
| 197 |
}
|
| 198 |
.preview-item span {
|
|
@@ -203,7 +203,6 @@
|
|
| 203 |
text-align: center;
|
| 204 |
}
|
| 205 |
|
| 206 |
-
|
| 207 |
.button {
|
| 208 |
width: 100%;
|
| 209 |
padding: var(--spacing-unit);
|
|
@@ -230,13 +229,12 @@
|
|
| 230 |
|
| 231 |
.clear-button {
|
| 232 |
background-color: var(--danger-color);
|
| 233 |
-
margin-top: 0;
|
| 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 |
}
|
|
@@ -245,6 +243,23 @@
|
|
| 245 |
background-color: var(--secondary-hover);
|
| 246 |
}
|
| 247 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
#solving-container {
|
| 249 |
display: none;
|
| 250 |
background-color: #f9f9f9;
|
|
@@ -383,6 +398,10 @@
|
|
| 383 |
</div>
|
| 384 |
</div>
|
| 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>
|
|
@@ -422,13 +441,66 @@
|
|
| 422 |
const copyButton = document.getElementById('copy-button');
|
| 423 |
const statusElement = document.getElementById('status');
|
| 424 |
const loadingText = document.getElementById('loading-text');
|
|
|
|
|
|
|
| 425 |
|
| 426 |
-
let selectedFiles = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
|
| 428 |
window.selectStyle = function(style) {
|
| 429 |
document.getElementById(`style-${style}`).checked = true;
|
| 430 |
};
|
| 431 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
uploadSection.addEventListener('click', () => fileInput.click());
|
| 433 |
|
| 434 |
uploadSection.addEventListener('dragover', (e) => {
|
|
@@ -464,15 +536,14 @@
|
|
| 464 |
|
| 465 |
newFiles.forEach(file => {
|
| 466 |
if (file.type.startsWith('image/')) {
|
| 467 |
-
if (!selectedFiles.some(sf => sf.name === file.name && sf.size === file.size)) {
|
| 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;
|
| 476 |
} else {
|
| 477 |
alert('Vous ne pouvez sélectionner qu\'un seul fichier PDF.');
|
| 478 |
}
|
|
@@ -489,7 +560,7 @@
|
|
| 489 |
}
|
| 490 |
|
| 491 |
function updateFilePreviews() {
|
| 492 |
-
filePreviewArea.innerHTML = '';
|
| 493 |
|
| 494 |
if (selectedFiles.length === 0) {
|
| 495 |
filePreviewArea.style.display = 'none';
|
|
@@ -504,7 +575,6 @@
|
|
| 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();
|
|
@@ -516,7 +586,7 @@
|
|
| 516 |
} else if (file.type === 'application/pdf') {
|
| 517 |
const pdfIcon = document.createElement('div');
|
| 518 |
pdfIcon.classList.add('pdf-icon');
|
| 519 |
-
pdfIcon.textContent = '📄';
|
| 520 |
previewItem.appendChild(pdfIcon);
|
| 521 |
}
|
| 522 |
previewItem.appendChild(fileNameSpan);
|
|
@@ -525,10 +595,14 @@
|
|
| 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';
|
|
@@ -538,7 +612,7 @@
|
|
| 538 |
|
| 539 |
clearFilesButton.addEventListener('click', () => {
|
| 540 |
selectedFiles = [];
|
| 541 |
-
fileInput.value = '';
|
| 542 |
updateFilePreviews();
|
| 543 |
updateButtonsState();
|
| 544 |
solvingContainer.style.display = 'none';
|
|
@@ -546,10 +620,13 @@
|
|
| 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 |
|
|
|
|
|
|
|
|
|
|
| 553 |
solveButton.disabled = true;
|
| 554 |
solveButton.textContent = '⏳ Traitement...';
|
| 555 |
solvingContainer.style.display = 'block';
|
|
@@ -561,14 +638,9 @@
|
|
| 561 |
|
| 562 |
const formData = new FormData();
|
| 563 |
selectedFiles.forEach(file => {
|
| 564 |
-
formData.append('user_files', file);
|
| 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',
|
|
@@ -595,7 +667,7 @@
|
|
| 595 |
|
| 596 |
if (data.error) {
|
| 597 |
handleError(data.error);
|
| 598 |
-
eventSource.close();
|
| 599 |
return;
|
| 600 |
}
|
| 601 |
|
|
@@ -633,8 +705,7 @@
|
|
| 633 |
case 'completed':
|
| 634 |
statusElement.className = 'status completed';
|
| 635 |
statusElement.textContent = 'Traitement terminé avec succès ! 🎉';
|
| 636 |
-
responseDiv.innerHTML =
|
| 637 |
-
renderMathInElement(responseDiv);
|
| 638 |
showResponse();
|
| 639 |
break;
|
| 640 |
case 'error':
|
|
@@ -646,14 +717,9 @@
|
|
| 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) {
|
|
@@ -670,7 +736,6 @@
|
|
| 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 |
})
|
|
@@ -690,7 +755,6 @@
|
|
| 690 |
}, 2000);
|
| 691 |
})
|
| 692 |
.catch(() => {
|
| 693 |
-
// Fallback pour les anciens navigateurs
|
| 694 |
const range = document.createRange();
|
| 695 |
range.selectNode(responseDiv);
|
| 696 |
window.getSelection().removeAllRanges();
|
|
|
|
| 192 |
object-fit: cover;
|
| 193 |
}
|
| 194 |
.preview-item .pdf-icon {
|
| 195 |
+
font-size: 3rem;
|
| 196 |
color: var(--danger-color);
|
| 197 |
}
|
| 198 |
.preview-item span {
|
|
|
|
| 203 |
text-align: center;
|
| 204 |
}
|
| 205 |
|
|
|
|
| 206 |
.button {
|
| 207 |
width: 100%;
|
| 208 |
padding: var(--spacing-unit);
|
|
|
|
| 229 |
|
| 230 |
.clear-button {
|
| 231 |
background-color: var(--danger-color);
|
| 232 |
+
margin-top: 0;
|
| 233 |
}
|
| 234 |
.clear-button:hover:not(:disabled) {
|
| 235 |
background-color: var(--danger-hover);
|
| 236 |
}
|
| 237 |
|
|
|
|
| 238 |
.copy-button {
|
| 239 |
background-color: var(--secondary-color);
|
| 240 |
}
|
|
|
|
| 243 |
background-color: var(--secondary-hover);
|
| 244 |
}
|
| 245 |
|
| 246 |
+
.cooldown-notice {
|
| 247 |
+
background-color: #fff3cd;
|
| 248 |
+
border: 1px solid #ffeaa7;
|
| 249 |
+
border-radius: 0.5rem;
|
| 250 |
+
padding: var(--spacing-unit);
|
| 251 |
+
margin: var(--spacing-unit) 0;
|
| 252 |
+
text-align: center;
|
| 253 |
+
color: #856404;
|
| 254 |
+
font-weight: 500;
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
.cooldown-timer {
|
| 258 |
+
font-size: 1.2rem;
|
| 259 |
+
color: #d63031;
|
| 260 |
+
font-weight: bold;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
#solving-container {
|
| 264 |
display: none;
|
| 265 |
background-color: #f9f9f9;
|
|
|
|
| 398 |
</div>
|
| 399 |
</div>
|
| 400 |
|
| 401 |
+
<div id="cooldown-notice" class="cooldown-notice" style="display: none;">
|
| 402 |
+
⏰ Veuillez attendre <span id="cooldown-timer" class="cooldown-timer">2:00</span> avant de pouvoir soumettre à nouveau.
|
| 403 |
+
</div>
|
| 404 |
+
|
| 405 |
<div id="upload-section" class="upload-section">
|
| 406 |
<div class="upload-icon">📤</div>
|
| 407 |
<p>Cliquez ou glissez-déposez vos images et/ou 1 fichier PDF ici</p>
|
|
|
|
| 441 |
const copyButton = document.getElementById('copy-button');
|
| 442 |
const statusElement = document.getElementById('status');
|
| 443 |
const loadingText = document.getElementById('loading-text');
|
| 444 |
+
const cooldownNotice = document.getElementById('cooldown-notice');
|
| 445 |
+
const cooldownTimer = document.getElementById('cooldown-timer');
|
| 446 |
|
| 447 |
+
let selectedFiles = [];
|
| 448 |
+
let cooldownEndTime = 0;
|
| 449 |
+
let cooldownInterval = null;
|
| 450 |
+
|
| 451 |
+
// Vérifier le cooldown au chargement de la page
|
| 452 |
+
checkCooldownOnLoad();
|
| 453 |
|
| 454 |
window.selectStyle = function(style) {
|
| 455 |
document.getElementById(`style-${style}`).checked = true;
|
| 456 |
};
|
| 457 |
|
| 458 |
+
function checkCooldownOnLoad() {
|
| 459 |
+
const savedCooldownEndTime = localStorage.getItem('mariamCooldownEndTime');
|
| 460 |
+
if (savedCooldownEndTime) {
|
| 461 |
+
const endTime = parseInt(savedCooldownEndTime);
|
| 462 |
+
const now = Date.now();
|
| 463 |
+
if (now < endTime) {
|
| 464 |
+
cooldownEndTime = endTime;
|
| 465 |
+
startCooldownTimer();
|
| 466 |
+
} else {
|
| 467 |
+
localStorage.removeItem('mariamCooldownEndTime');
|
| 468 |
+
}
|
| 469 |
+
}
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
function startCooldown() {
|
| 473 |
+
cooldownEndTime = Date.now() + (2 * 60 * 1000); // 2 minutes
|
| 474 |
+
localStorage.setItem('mariamCooldownEndTime', cooldownEndTime.toString());
|
| 475 |
+
startCooldownTimer();
|
| 476 |
+
}
|
| 477 |
+
|
| 478 |
+
function startCooldownTimer() {
|
| 479 |
+
cooldownNotice.style.display = 'block';
|
| 480 |
+
solveButton.disabled = true;
|
| 481 |
+
|
| 482 |
+
cooldownInterval = setInterval(() => {
|
| 483 |
+
const now = Date.now();
|
| 484 |
+
const remainingTime = Math.max(0, cooldownEndTime - now);
|
| 485 |
+
|
| 486 |
+
if (remainingTime <= 0) {
|
| 487 |
+
clearInterval(cooldownInterval);
|
| 488 |
+
cooldownNotice.style.display = 'none';
|
| 489 |
+
localStorage.removeItem('mariamCooldownEndTime');
|
| 490 |
+
updateButtonsState(); // Réactiver le bouton si des fichiers sont sélectionnés
|
| 491 |
+
return;
|
| 492 |
+
}
|
| 493 |
+
|
| 494 |
+
const minutes = Math.floor(remainingTime / 60000);
|
| 495 |
+
const seconds = Math.floor((remainingTime % 60000) / 1000);
|
| 496 |
+
cooldownTimer.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
| 497 |
+
}, 1000);
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
function isCooldownActive() {
|
| 501 |
+
return Date.now() < cooldownEndTime;
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
uploadSection.addEventListener('click', () => fileInput.click());
|
| 505 |
|
| 506 |
uploadSection.addEventListener('dragover', (e) => {
|
|
|
|
| 536 |
|
| 537 |
newFiles.forEach(file => {
|
| 538 |
if (file.type.startsWith('image/')) {
|
| 539 |
+
if (!selectedFiles.some(sf => sf.name === file.name && sf.size === file.size)) {
|
| 540 |
selectedFiles.push(file);
|
| 541 |
}
|
| 542 |
} else if (file.type === 'application/pdf') {
|
| 543 |
if (!pdfAlreadySelected) {
|
|
|
|
| 544 |
selectedFiles = selectedFiles.filter(f => f.type !== 'application/pdf');
|
| 545 |
selectedFiles.push(file);
|
| 546 |
+
pdfAlreadySelected = true;
|
| 547 |
} else {
|
| 548 |
alert('Vous ne pouvez sélectionner qu\'un seul fichier PDF.');
|
| 549 |
}
|
|
|
|
| 560 |
}
|
| 561 |
|
| 562 |
function updateFilePreviews() {
|
| 563 |
+
filePreviewArea.innerHTML = '';
|
| 564 |
|
| 565 |
if (selectedFiles.length === 0) {
|
| 566 |
filePreviewArea.style.display = 'none';
|
|
|
|
| 575 |
const fileNameSpan = document.createElement('span');
|
| 576 |
fileNameSpan.textContent = file.name.length > 15 ? file.name.substring(0,12) + "..." : file.name;
|
| 577 |
|
|
|
|
| 578 |
if (file.type.startsWith('image/')) {
|
| 579 |
const img = document.createElement('img');
|
| 580 |
const reader = new FileReader();
|
|
|
|
| 586 |
} else if (file.type === 'application/pdf') {
|
| 587 |
const pdfIcon = document.createElement('div');
|
| 588 |
pdfIcon.classList.add('pdf-icon');
|
| 589 |
+
pdfIcon.textContent = '📄';
|
| 590 |
previewItem.appendChild(pdfIcon);
|
| 591 |
}
|
| 592 |
previewItem.appendChild(fileNameSpan);
|
|
|
|
| 595 |
}
|
| 596 |
|
| 597 |
function updateButtonsState() {
|
| 598 |
+
if (selectedFiles.length > 0 && !isCooldownActive()) {
|
| 599 |
solveButton.disabled = false;
|
| 600 |
solveButton.textContent = `🔍 Résoudre (${selectedFiles.length} fichier(s))`;
|
| 601 |
clearFilesButton.style.display = 'block';
|
| 602 |
+
} else if (selectedFiles.length > 0 && isCooldownActive()) {
|
| 603 |
+
solveButton.disabled = true;
|
| 604 |
+
solveButton.textContent = `🔍 Résoudre (${selectedFiles.length} fichier(s))`;
|
| 605 |
+
clearFilesButton.style.display = 'block';
|
| 606 |
} else {
|
| 607 |
solveButton.disabled = true;
|
| 608 |
solveButton.textContent = '🔍 Résoudre';
|
|
|
|
| 612 |
|
| 613 |
clearFilesButton.addEventListener('click', () => {
|
| 614 |
selectedFiles = [];
|
| 615 |
+
fileInput.value = '';
|
| 616 |
updateFilePreviews();
|
| 617 |
updateButtonsState();
|
| 618 |
solvingContainer.style.display = 'none';
|
|
|
|
| 620 |
});
|
| 621 |
|
| 622 |
solveButton.addEventListener('click', () => {
|
| 623 |
+
if (selectedFiles.length === 0 || isCooldownActive()) return;
|
| 624 |
|
| 625 |
const selectedStyle = document.querySelector('input[name="resolution-style"]:checked').value;
|
| 626 |
|
| 627 |
+
// Démarrer le cooldown immédiatement
|
| 628 |
+
startCooldown();
|
| 629 |
+
|
| 630 |
solveButton.disabled = true;
|
| 631 |
solveButton.textContent = '⏳ Traitement...';
|
| 632 |
solvingContainer.style.display = 'block';
|
|
|
|
| 638 |
|
| 639 |
const formData = new FormData();
|
| 640 |
selectedFiles.forEach(file => {
|
| 641 |
+
formData.append('user_files', file);
|
| 642 |
});
|
| 643 |
formData.append('style', selectedStyle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 644 |
|
| 645 |
fetch('/solve', {
|
| 646 |
method: 'POST',
|
|
|
|
| 667 |
|
| 668 |
if (data.error) {
|
| 669 |
handleError(data.error);
|
| 670 |
+
eventSource.close();
|
| 671 |
return;
|
| 672 |
}
|
| 673 |
|
|
|
|
| 705 |
case 'completed':
|
| 706 |
statusElement.className = 'status completed';
|
| 707 |
statusElement.textContent = 'Traitement terminé avec succès ! 🎉';
|
| 708 |
+
responseDiv.innerHTML = '<p style="color: #2ecc71; font-size: 1.2rem; text-align: center; font-weight: bold;">📄 Votre PDF est disponible sur Telegram</p>';
|
|
|
|
| 709 |
showResponse();
|
| 710 |
break;
|
| 711 |
case 'error':
|
|
|
|
| 717 |
function showResponse() {
|
| 718 |
responseContainer.style.display = 'block';
|
| 719 |
loadingText.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 720 |
}
|
| 721 |
|
| 722 |
function handleEventSourceError(taskId) {
|
|
|
|
| 723 |
fetch('/task/' + taskId)
|
| 724 |
.then(response => {
|
| 725 |
if (!response.ok) {
|
|
|
|
| 736 |
} else if (taskData.status === 'error' || taskData.error) {
|
| 737 |
handleError(taskData.error || 'Une erreur est survenue lors du traitement de la tâche.');
|
| 738 |
} else {
|
|
|
|
| 739 |
handleError('La connexion au flux a été perdue. Vérifiez Telegram ou réessayez plus tard.');
|
| 740 |
}
|
| 741 |
})
|
|
|
|
| 755 |
}, 2000);
|
| 756 |
})
|
| 757 |
.catch(() => {
|
|
|
|
| 758 |
const range = document.createRange();
|
| 759 |
range.selectNode(responseDiv);
|
| 760 |
window.getSelection().removeAllRanges();
|