Update templates/index.html
Browse files- templates/index.html +76 -12
templates/index.html
CHANGED
|
@@ -5,6 +5,7 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>Générateur de Manga BD</title>
|
| 7 |
<style>
|
|
|
|
| 8 |
* {
|
| 9 |
margin: 0;
|
| 10 |
padding: 0;
|
|
@@ -217,6 +218,31 @@
|
|
| 217 |
border: 2px solid #68d391;
|
| 218 |
}
|
| 219 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
@media (max-width: 768px) {
|
| 221 |
.main-content {
|
| 222 |
grid-template-columns: 1fr;
|
|
@@ -241,6 +267,7 @@
|
|
| 241 |
</div>
|
| 242 |
|
| 243 |
<div class="main-content">
|
|
|
|
| 244 |
<div class="input-section">
|
| 245 |
<h2>📝 Configuration du Manga</h2>
|
| 246 |
<textarea
|
|
@@ -257,7 +284,6 @@
|
|
| 257 |
🚀 Générer le Manga
|
| 258 |
</button>
|
| 259 |
</div>
|
| 260 |
-
|
| 261 |
<div class="status-section">
|
| 262 |
<h2>📊 Statut de Génération</h2>
|
| 263 |
<div id="statusContainer">
|
|
@@ -268,7 +294,16 @@
|
|
| 268 |
</div>
|
| 269 |
</div>
|
| 270 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
|
|
|
|
| 272 |
<div class="example-section">
|
| 273 |
<h2>📋 Format JSON Attendu</h2>
|
| 274 |
<p style="margin-bottom: 20px;">
|
|
@@ -289,21 +324,29 @@
|
|
| 289 |
</div>
|
| 290 |
|
| 291 |
<script>
|
|
|
|
| 292 |
let currentTaskId = null;
|
| 293 |
let statusInterval = null;
|
| 294 |
|
| 295 |
const generateBtn = document.getElementById('generateBtn');
|
| 296 |
const jsonInput = document.getElementById('jsonInput');
|
| 297 |
const statusContainer = document.getElementById('statusContainer');
|
|
|
|
|
|
|
| 298 |
|
| 299 |
generateBtn.addEventListener('click', async () => {
|
| 300 |
-
|
| 301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
if (!jsonText) {
|
| 303 |
showAlert('Veuillez saisir une configuration JSON', 'error');
|
| 304 |
return;
|
| 305 |
}
|
| 306 |
-
|
| 307 |
let jsonData;
|
| 308 |
try {
|
| 309 |
jsonData = JSON.parse(jsonText);
|
|
@@ -348,6 +391,7 @@
|
|
| 348 |
}
|
| 349 |
});
|
| 350 |
|
|
|
|
| 351 |
function startStatusPolling() {
|
| 352 |
if (statusInterval) {
|
| 353 |
clearInterval(statusInterval);
|
|
@@ -378,6 +422,7 @@
|
|
| 378 |
}, 2000); // Vérifier toutes les 2 secondes
|
| 379 |
}
|
| 380 |
|
|
|
|
| 381 |
function updateStatusDisplay(status) {
|
| 382 |
const container = statusContainer;
|
| 383 |
|
|
@@ -396,10 +441,10 @@
|
|
| 396 |
statusIcon = '🎨';
|
| 397 |
statusText = 'Génération en cours';
|
| 398 |
break;
|
| 399 |
-
case '
|
| 400 |
statusClass = 'generating';
|
| 401 |
-
statusIcon = '
|
| 402 |
-
statusText = 'Création
|
| 403 |
break;
|
| 404 |
case 'completed':
|
| 405 |
statusClass = 'completed';
|
|
@@ -413,6 +458,7 @@
|
|
| 413 |
break;
|
| 414 |
}
|
| 415 |
|
|
|
|
| 416 |
let progressHtml = '';
|
| 417 |
if (status.total_pages && status.current_page) {
|
| 418 |
const progress = (status.current_page / status.total_pages) * 100;
|
|
@@ -431,16 +477,18 @@
|
|
| 431 |
if (status.status === 'completed') {
|
| 432 |
downloadHtml = `
|
| 433 |
<a href="/download/${currentTaskId}" class="download-btn" style="width: 100%; text-align: center; margin-top: 15px;">
|
| 434 |
-
📥 Télécharger
|
| 435 |
</a>
|
| 436 |
`;
|
| 437 |
}
|
| 438 |
|
|
|
|
| 439 |
let errorHtml = '';
|
| 440 |
if (status.error) {
|
| 441 |
errorHtml = `<div class="alert alert-error">${status.error}</div>`;
|
| 442 |
}
|
| 443 |
-
|
|
|
|
| 444 |
container.innerHTML = `
|
| 445 |
<div class="status-card ${statusClass}">
|
| 446 |
<h3>${statusIcon} ${statusText}</h3>
|
|
@@ -452,15 +500,33 @@
|
|
| 452 |
${downloadHtml}
|
| 453 |
</div>
|
| 454 |
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
}
|
| 456 |
|
|
|
|
| 457 |
function showAlert(message, type) {
|
| 458 |
const alertClass = type === 'error' ? 'alert-error' : 'alert-success';
|
| 459 |
const alertHtml = `<div class="alert ${alertClass}">${message}</div>`;
|
| 460 |
|
| 461 |
statusContainer.innerHTML = alertHtml + statusContainer.innerHTML;
|
| 462 |
|
| 463 |
-
// Retirer l'alerte après 5 secondes
|
| 464 |
setTimeout(() => {
|
| 465 |
const alert = statusContainer.querySelector('.alert');
|
| 466 |
if (alert) {
|
|
@@ -469,20 +535,18 @@
|
|
| 469 |
}, 5000);
|
| 470 |
}
|
| 471 |
|
| 472 |
-
// Nettoyer l'intervalle quand la page est fermée
|
| 473 |
window.addEventListener('beforeunload', () => {
|
| 474 |
if (statusInterval) {
|
| 475 |
clearInterval(statusInterval);
|
| 476 |
}
|
| 477 |
});
|
| 478 |
|
| 479 |
-
// Auto-formatter le JSON
|
| 480 |
jsonInput.addEventListener('blur', () => {
|
| 481 |
try {
|
| 482 |
const parsed = JSON.parse(jsonInput.value);
|
| 483 |
jsonInput.value = JSON.stringify(parsed, null, 2);
|
| 484 |
} catch (error) {
|
| 485 |
-
// Ignorer les erreurs
|
| 486 |
}
|
| 487 |
});
|
| 488 |
</script>
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>Générateur de Manga BD</title>
|
| 7 |
<style>
|
| 8 |
+
/* ... (TOUT VOTRE CSS RESTE IDENTIQUE) ... */
|
| 9 |
* {
|
| 10 |
margin: 0;
|
| 11 |
padding: 0;
|
|
|
|
| 218 |
border: 2px solid #68d391;
|
| 219 |
}
|
| 220 |
|
| 221 |
+
/* NOUVEAUX STYLES POUR LA GRILLE D'IMAGES */
|
| 222 |
+
.preview-section {
|
| 223 |
+
background: rgba(255, 255, 255, 0.95);
|
| 224 |
+
padding: 30px;
|
| 225 |
+
border-radius: 15px;
|
| 226 |
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
| 227 |
+
margin-top: 30px;
|
| 228 |
+
display: none; /* Caché par défaut */
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
.image-grid {
|
| 232 |
+
display: grid;
|
| 233 |
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
| 234 |
+
gap: 20px;
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
.image-grid img {
|
| 238 |
+
width: 100%;
|
| 239 |
+
height: auto;
|
| 240 |
+
border-radius: 10px;
|
| 241 |
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
| 242 |
+
object-fit: cover;
|
| 243 |
+
aspect-ratio: 2 / 3; /* Ratio commun pour les pages de manga */
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
@media (max-width: 768px) {
|
| 247 |
.main-content {
|
| 248 |
grid-template-columns: 1fr;
|
|
|
|
| 267 |
</div>
|
| 268 |
|
| 269 |
<div class="main-content">
|
| 270 |
+
<!-- ... (La section input reste la même) ... -->
|
| 271 |
<div class="input-section">
|
| 272 |
<h2>📝 Configuration du Manga</h2>
|
| 273 |
<textarea
|
|
|
|
| 284 |
🚀 Générer le Manga
|
| 285 |
</button>
|
| 286 |
</div>
|
|
|
|
| 287 |
<div class="status-section">
|
| 288 |
<h2>📊 Statut de Génération</h2>
|
| 289 |
<div id="statusContainer">
|
|
|
|
| 294 |
</div>
|
| 295 |
</div>
|
| 296 |
</div>
|
| 297 |
+
|
| 298 |
+
<!-- NOUVELLE SECTION POUR L'AFFICHAGE DES IMAGES -->
|
| 299 |
+
<div id="previewSection" class="preview-section">
|
| 300 |
+
<h2>🖼️ Prévisualisation des Pages</h2>
|
| 301 |
+
<div id="imageGrid" class="image-grid">
|
| 302 |
+
<!-- Les images générées apparaîtront ici -->
|
| 303 |
+
</div>
|
| 304 |
+
</div>
|
| 305 |
|
| 306 |
+
<!-- ... (La section exemple reste la même) ... -->
|
| 307 |
<div class="example-section">
|
| 308 |
<h2>📋 Format JSON Attendu</h2>
|
| 309 |
<p style="margin-bottom: 20px;">
|
|
|
|
| 324 |
</div>
|
| 325 |
|
| 326 |
<script>
|
| 327 |
+
// ... (Les variables globales restent les mêmes) ...
|
| 328 |
let currentTaskId = null;
|
| 329 |
let statusInterval = null;
|
| 330 |
|
| 331 |
const generateBtn = document.getElementById('generateBtn');
|
| 332 |
const jsonInput = document.getElementById('jsonInput');
|
| 333 |
const statusContainer = document.getElementById('statusContainer');
|
| 334 |
+
const previewSection = document.getElementById('previewSection');
|
| 335 |
+
const imageGrid = document.getElementById('imageGrid');
|
| 336 |
|
| 337 |
generateBtn.addEventListener('click', async () => {
|
| 338 |
+
// ... (Cette fonction reste quasi identique, on ajoute juste un reset de l'affichage)
|
| 339 |
|
| 340 |
+
// Reset de l'interface
|
| 341 |
+
imageGrid.innerHTML = '';
|
| 342 |
+
previewSection.style.display = 'none';
|
| 343 |
+
|
| 344 |
+
const jsonText = jsonInput.value.trim();
|
| 345 |
if (!jsonText) {
|
| 346 |
showAlert('Veuillez saisir une configuration JSON', 'error');
|
| 347 |
return;
|
| 348 |
}
|
| 349 |
+
// ... (Le reste du code de la fonction est inchangé)
|
| 350 |
let jsonData;
|
| 351 |
try {
|
| 352 |
jsonData = JSON.parse(jsonText);
|
|
|
|
| 391 |
}
|
| 392 |
});
|
| 393 |
|
| 394 |
+
// La fonction startStatusPolling reste la même
|
| 395 |
function startStatusPolling() {
|
| 396 |
if (statusInterval) {
|
| 397 |
clearInterval(statusInterval);
|
|
|
|
| 422 |
}, 2000); // Vérifier toutes les 2 secondes
|
| 423 |
}
|
| 424 |
|
| 425 |
+
// MISE À JOUR MAJEURE DE CETTE FONCTION
|
| 426 |
function updateStatusDisplay(status) {
|
| 427 |
const container = statusContainer;
|
| 428 |
|
|
|
|
| 441 |
statusIcon = '🎨';
|
| 442 |
statusText = 'Génération en cours';
|
| 443 |
break;
|
| 444 |
+
case 'creating_zip': // STATUT MIS À JOUR
|
| 445 |
statusClass = 'generating';
|
| 446 |
+
statusIcon = '🗜️'; // Icône de compression
|
| 447 |
+
statusText = 'Création de l\'archive ZIP';
|
| 448 |
break;
|
| 449 |
case 'completed':
|
| 450 |
statusClass = 'completed';
|
|
|
|
| 458 |
break;
|
| 459 |
}
|
| 460 |
|
| 461 |
+
// ... (La logique de la barre de progression reste la même)
|
| 462 |
let progressHtml = '';
|
| 463 |
if (status.total_pages && status.current_page) {
|
| 464 |
const progress = (status.current_page / status.total_pages) * 100;
|
|
|
|
| 477 |
if (status.status === 'completed') {
|
| 478 |
downloadHtml = `
|
| 479 |
<a href="/download/${currentTaskId}" class="download-btn" style="width: 100%; text-align: center; margin-top: 15px;">
|
| 480 |
+
📥 Télécharger l'archive ZIP
|
| 481 |
</a>
|
| 482 |
`;
|
| 483 |
}
|
| 484 |
|
| 485 |
+
// ... (Le HTML pour l'erreur reste le même)
|
| 486 |
let errorHtml = '';
|
| 487 |
if (status.error) {
|
| 488 |
errorHtml = `<div class="alert alert-error">${status.error}</div>`;
|
| 489 |
}
|
| 490 |
+
|
| 491 |
+
// Affichage du statut
|
| 492 |
container.innerHTML = `
|
| 493 |
<div class="status-card ${statusClass}">
|
| 494 |
<h3>${statusIcon} ${statusText}</h3>
|
|
|
|
| 500 |
${downloadHtml}
|
| 501 |
</div>
|
| 502 |
`;
|
| 503 |
+
|
| 504 |
+
// NOUVELLE PARTIE : Affichage des images
|
| 505 |
+
if (status.image_urls && status.image_urls.length > 0) {
|
| 506 |
+
previewSection.style.display = 'block';
|
| 507 |
+
|
| 508 |
+
status.image_urls.forEach(url => {
|
| 509 |
+
// Vérifier si l'image n'est pas déjà affichée
|
| 510 |
+
if (!document.querySelector(`img[src="${url}"]`)) {
|
| 511 |
+
const img = document.createElement('img');
|
| 512 |
+
img.src = url;
|
| 513 |
+
img.alt = `Page générée pour la tâche ${currentTaskId}`;
|
| 514 |
+
img.onload = () => img.style.opacity = 1; // Effet d'apparition
|
| 515 |
+
img.style.opacity = 0;
|
| 516 |
+
img.style.transition = 'opacity 0.5s';
|
| 517 |
+
imageGrid.appendChild(img);
|
| 518 |
+
}
|
| 519 |
+
});
|
| 520 |
+
}
|
| 521 |
}
|
| 522 |
|
| 523 |
+
// ... (Le reste du script JS reste identique)
|
| 524 |
function showAlert(message, type) {
|
| 525 |
const alertClass = type === 'error' ? 'alert-error' : 'alert-success';
|
| 526 |
const alertHtml = `<div class="alert ${alertClass}">${message}</div>`;
|
| 527 |
|
| 528 |
statusContainer.innerHTML = alertHtml + statusContainer.innerHTML;
|
| 529 |
|
|
|
|
| 530 |
setTimeout(() => {
|
| 531 |
const alert = statusContainer.querySelector('.alert');
|
| 532 |
if (alert) {
|
|
|
|
| 535 |
}, 5000);
|
| 536 |
}
|
| 537 |
|
|
|
|
| 538 |
window.addEventListener('beforeunload', () => {
|
| 539 |
if (statusInterval) {
|
| 540 |
clearInterval(statusInterval);
|
| 541 |
}
|
| 542 |
});
|
| 543 |
|
|
|
|
| 544 |
jsonInput.addEventListener('blur', () => {
|
| 545 |
try {
|
| 546 |
const parsed = JSON.parse(jsonInput.value);
|
| 547 |
jsonInput.value = JSON.stringify(parsed, null, 2);
|
| 548 |
} catch (error) {
|
| 549 |
+
// Ignorer les erreurs
|
| 550 |
}
|
| 551 |
});
|
| 552 |
</script>
|