Update templates/index.html
Browse files- templates/index.html +53 -22
templates/index.html
CHANGED
|
@@ -22,6 +22,10 @@
|
|
| 22 |
#historyList::-webkit-scrollbar-track { background: #e0e7ff; }
|
| 23 |
#historyList::-webkit-scrollbar-thumb { background: #a5b4fc; border-radius: 3px; }
|
| 24 |
#historyList::-webkit-scrollbar-thumb:hover { background: #818cf8; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
</style>
|
| 26 |
</head>
|
| 27 |
<body class="min-h-screen text-slate-800">
|
|
@@ -29,7 +33,7 @@
|
|
| 29 |
<!-- Header -->
|
| 30 |
<header class="text-center mb-10 md:mb-12 fade-in-up">
|
| 31 |
<h1 class="text-4xl sm:text-5xl md:text-6xl font-extrabold text-slate-900 mb-3">Synthétiseur AI Pro</h1>
|
| 32 |
-
<p class="text-base sm:text-lg text-slate-600 max-w-2xl mx-auto">Transformez vos documents en résumés clairs et concis.</p>
|
| 33 |
</header>
|
| 34 |
|
| 35 |
<!-- Main Content -->
|
|
@@ -40,8 +44,8 @@
|
|
| 40 |
<div class="bg-white/80 backdrop-blur-sm border border-slate-200 rounded-2xl shadow-lg p-6 md:p-8 fade-in-up" style="animation-delay: 100ms;">
|
| 41 |
<form id="uploadForm" class="space-y-6">
|
| 42 |
<div>
|
| 43 |
-
<h2 class="text-xl md:text-2xl font-bold text-slate-800 mb-1">1.
|
| 44 |
-
<p class="text-slate-500 mb-6">
|
| 45 |
|
| 46 |
<label for="fileInput" id="dropzone" class="group block border-2 border-dashed border-slate-300 rounded-xl p-8 text-center transition-all duration-300 hover:border-blue-500 hover:bg-blue-50 cursor-pointer">
|
| 47 |
<input type="file" id="fileInput" name="file" class="hidden" accept=".pdf,.mp3,.mp4,.wav,.txt,.doc,.docx">
|
|
@@ -51,6 +55,13 @@
|
|
| 51 |
<p class="text-xs sm:text-sm">PDF, MP3, MP4, WAV, TXT, DOCX (Max 50MB)</p>
|
| 52 |
</div>
|
| 53 |
</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
</div>
|
| 55 |
|
| 56 |
<div id="fileInfo" class="hidden items-center justify-between bg-slate-100 rounded-lg p-3">
|
|
@@ -143,17 +154,12 @@
|
|
| 143 |
const { jsPDF } = window.jspdf;
|
| 144 |
|
| 145 |
// DOM Elements
|
| 146 |
-
const fileIconContainer = document.getElementById('fileIconContainer');
|
| 147 |
-
const summaryContent = document.getElementById('summaryContent');
|
| 148 |
-
const summaryContentWrapper = document.getElementById('summaryContentWrapper');
|
| 149 |
-
const downloadBtn = document.getElementById('downloadBtn');
|
| 150 |
-
const downloadBtnText = document.getElementById('downloadBtnText');
|
| 151 |
-
const downloadBtnIcon = document.getElementById('downloadBtnIcon');
|
| 152 |
-
const downloadBtnSpinner = document.getElementById('downloadBtnSpinner');
|
| 153 |
const uploadForm = document.getElementById('uploadForm');
|
| 154 |
const fileInput = document.getElementById('fileInput');
|
|
|
|
| 155 |
const dropzone = document.getElementById('dropzone');
|
| 156 |
const fileInfo = document.getElementById('fileInfo');
|
|
|
|
| 157 |
const fileNameText = document.getElementById('fileNameText');
|
| 158 |
const fileSizeText = document.getElementById('fileSizeText');
|
| 159 |
const removeFileBtn = document.getElementById('removeFileBtn');
|
|
@@ -161,17 +167,22 @@
|
|
| 161 |
const btnText = document.getElementById('btnText');
|
| 162 |
const btnSpinner = document.getElementById('btnSpinner');
|
| 163 |
const resultCard = document.getElementById('resultCard');
|
| 164 |
-
const errorCard = document.getElementById('errorCard');
|
| 165 |
const resultFilename = document.getElementById('resultFilename');
|
|
|
|
|
|
|
|
|
|
| 166 |
const errorMessage = document.getElementById('errorMessage');
|
| 167 |
const copyBtn = document.getElementById('copyBtn');
|
| 168 |
const copyBtnText = document.getElementById('copyBtnText');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
const historyList = document.getElementById('historyList');
|
| 170 |
const historyEmptyState = document.getElementById('historyEmptyState');
|
| 171 |
const clearHistoryBtn = document.getElementById('clearHistory');
|
| 172 |
|
| 173 |
let currentHistoryId = null;
|
| 174 |
-
let originalFile = null;
|
| 175 |
|
| 176 |
// --- UI Helper Functions ---
|
| 177 |
function showLoading(isLoading) {
|
|
@@ -192,6 +203,9 @@
|
|
| 192 |
}
|
| 193 |
|
| 194 |
function getFileIcon(filename, sizeClass = 'w-6 h-6') {
|
|
|
|
|
|
|
|
|
|
| 195 |
const extension = filename.split('.').pop().toLowerCase();
|
| 196 |
let iconSvg = '';
|
| 197 |
switch (extension) {
|
|
@@ -202,11 +216,17 @@
|
|
| 202 |
}
|
| 203 |
return iconSvg;
|
| 204 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
// --- Event Listeners & Core Logic ---
|
| 207 |
function handleFileSelect(file) {
|
| 208 |
if (!file) return;
|
| 209 |
-
|
| 210 |
fileNameText.textContent = file.name;
|
| 211 |
fileSizeText.textContent = `${(file.size / 1024 / 1024).toFixed(2)} MB`;
|
| 212 |
fileIconContainer.innerHTML = getFileIcon(file.name);
|
|
@@ -225,25 +245,36 @@
|
|
| 225 |
fileInput.files = e.dataTransfer.files;
|
| 226 |
handleFileSelect(file);
|
| 227 |
});
|
| 228 |
-
removeFileBtn.addEventListener('click',
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
|
|
|
|
|
|
| 232 |
});
|
| 233 |
|
| 234 |
uploadForm.addEventListener('submit', async (e) => {
|
| 235 |
e.preventDefault();
|
| 236 |
-
if (!fileInput.files[0]
|
|
|
|
|
|
|
|
|
|
| 237 |
showLoading(true);
|
| 238 |
-
resultCard.classList.add('hidden');
|
|
|
|
|
|
|
| 239 |
const formData = new FormData(uploadForm);
|
|
|
|
| 240 |
try {
|
| 241 |
const response = await fetch('/upload', { method: 'POST', body: formData });
|
| 242 |
const data = await response.json();
|
| 243 |
if (!response.ok || data.error) throw new Error(data.error || `Erreur serveur: ${response.status}`);
|
| 244 |
-
|
|
|
|
|
|
|
| 245 |
displaySummary(newItem);
|
| 246 |
loadHistory();
|
|
|
|
| 247 |
} catch (err) {
|
| 248 |
errorMessage.textContent = err.message;
|
| 249 |
errorCard.classList.remove('hidden'); errorCard.classList.add('fade-in-up');
|
|
@@ -284,7 +315,7 @@
|
|
| 284 |
heightLeft -= pdfHeight;
|
| 285 |
}
|
| 286 |
|
| 287 |
-
const safeFilename = resultFilename.textContent.replace(
|
| 288 |
pdf.save(safeFilename);
|
| 289 |
|
| 290 |
} catch (err) {
|
|
@@ -294,7 +325,7 @@
|
|
| 294 |
downloadBtn.disabled = false;
|
| 295 |
downloadBtnText.textContent = 'Télécharger PDF';
|
| 296 |
downloadBtnIcon.classList.remove('hidden');
|
| 297 |
-
downloadBtnSpinner.
|
| 298 |
}
|
| 299 |
});
|
| 300 |
|
|
|
|
| 22 |
#historyList::-webkit-scrollbar-track { background: #e0e7ff; }
|
| 23 |
#historyList::-webkit-scrollbar-thumb { background: #a5b4fc; border-radius: 3px; }
|
| 24 |
#historyList::-webkit-scrollbar-thumb:hover { background: #818cf8; }
|
| 25 |
+
.divider { display: flex; align-items: center; text-align: center; margin: 1.25rem 0; color: #94a3b8; }
|
| 26 |
+
.divider::before, .divider::after { content: ''; flex: 1; border-bottom: 1px solid #e2e8f0; }
|
| 27 |
+
.divider:not(:empty)::before { margin-right: .75em; }
|
| 28 |
+
.divider:not(:empty)::after { margin-left: .75em; }
|
| 29 |
</style>
|
| 30 |
</head>
|
| 31 |
<body class="min-h-screen text-slate-800">
|
|
|
|
| 33 |
<!-- Header -->
|
| 34 |
<header class="text-center mb-10 md:mb-12 fade-in-up">
|
| 35 |
<h1 class="text-4xl sm:text-5xl md:text-6xl font-extrabold text-slate-900 mb-3">Synthétiseur AI Pro</h1>
|
| 36 |
+
<p class="text-base sm:text-lg text-slate-600 max-w-2xl mx-auto">Transformez vos documents et vidéos en résumés clairs et concis.</p>
|
| 37 |
</header>
|
| 38 |
|
| 39 |
<!-- Main Content -->
|
|
|
|
| 44 |
<div class="bg-white/80 backdrop-blur-sm border border-slate-200 rounded-2xl shadow-lg p-6 md:p-8 fade-in-up" style="animation-delay: 100ms;">
|
| 45 |
<form id="uploadForm" class="space-y-6">
|
| 46 |
<div>
|
| 47 |
+
<h2 class="text-xl md:text-2xl font-bold text-slate-800 mb-1">1. Choisissez votre source</h2>
|
| 48 |
+
<p class="text-slate-500 mb-6">Importez un fichier ou collez un lien YouTube.</p>
|
| 49 |
|
| 50 |
<label for="fileInput" id="dropzone" class="group block border-2 border-dashed border-slate-300 rounded-xl p-8 text-center transition-all duration-300 hover:border-blue-500 hover:bg-blue-50 cursor-pointer">
|
| 51 |
<input type="file" id="fileInput" name="file" class="hidden" accept=".pdf,.mp3,.mp4,.wav,.txt,.doc,.docx">
|
|
|
|
| 55 |
<p class="text-xs sm:text-sm">PDF, MP3, MP4, WAV, TXT, DOCX (Max 50MB)</p>
|
| 56 |
</div>
|
| 57 |
</label>
|
| 58 |
+
|
| 59 |
+
<div class="divider">OU</div>
|
| 60 |
+
|
| 61 |
+
<div>
|
| 62 |
+
<label for="youtubeUrlInput" class="block text-sm font-medium text-slate-700 mb-2">Pour une vidéo YouTube</label>
|
| 63 |
+
<input type="url" id="youtubeUrlInput" name="youtube_url" class="block w-full px-4 py-2.5 text-slate-800 bg-slate-50 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition-colors" placeholder="https://www.youtube.com/watch?v=...">
|
| 64 |
+
</div>
|
| 65 |
</div>
|
| 66 |
|
| 67 |
<div id="fileInfo" class="hidden items-center justify-between bg-slate-100 rounded-lg p-3">
|
|
|
|
| 154 |
const { jsPDF } = window.jspdf;
|
| 155 |
|
| 156 |
// DOM Elements
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
const uploadForm = document.getElementById('uploadForm');
|
| 158 |
const fileInput = document.getElementById('fileInput');
|
| 159 |
+
const youtubeUrlInput = document.getElementById('youtubeUrlInput');
|
| 160 |
const dropzone = document.getElementById('dropzone');
|
| 161 |
const fileInfo = document.getElementById('fileInfo');
|
| 162 |
+
const fileIconContainer = document.getElementById('fileIconContainer');
|
| 163 |
const fileNameText = document.getElementById('fileNameText');
|
| 164 |
const fileSizeText = document.getElementById('fileSizeText');
|
| 165 |
const removeFileBtn = document.getElementById('removeFileBtn');
|
|
|
|
| 167 |
const btnText = document.getElementById('btnText');
|
| 168 |
const btnSpinner = document.getElementById('btnSpinner');
|
| 169 |
const resultCard = document.getElementById('resultCard');
|
|
|
|
| 170 |
const resultFilename = document.getElementById('resultFilename');
|
| 171 |
+
const summaryContent = document.getElementById('summaryContent');
|
| 172 |
+
const summaryContentWrapper = document.getElementById('summaryContentWrapper');
|
| 173 |
+
const errorCard = document.getElementById('errorCard');
|
| 174 |
const errorMessage = document.getElementById('errorMessage');
|
| 175 |
const copyBtn = document.getElementById('copyBtn');
|
| 176 |
const copyBtnText = document.getElementById('copyBtnText');
|
| 177 |
+
const downloadBtn = document.getElementById('downloadBtn');
|
| 178 |
+
const downloadBtnText = document.getElementById('downloadBtnText');
|
| 179 |
+
const downloadBtnIcon = document.getElementById('downloadBtnIcon');
|
| 180 |
+
const downloadBtnSpinner = document.getElementById('downloadBtnSpinner');
|
| 181 |
const historyList = document.getElementById('historyList');
|
| 182 |
const historyEmptyState = document.getElementById('historyEmptyState');
|
| 183 |
const clearHistoryBtn = document.getElementById('clearHistory');
|
| 184 |
|
| 185 |
let currentHistoryId = null;
|
|
|
|
| 186 |
|
| 187 |
// --- UI Helper Functions ---
|
| 188 |
function showLoading(isLoading) {
|
|
|
|
| 203 |
}
|
| 204 |
|
| 205 |
function getFileIcon(filename, sizeClass = 'w-6 h-6') {
|
| 206 |
+
if (filename === 'Vidéo YouTube') {
|
| 207 |
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="${sizeClass} text-red-600"><path d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"></path></svg>`;
|
| 208 |
+
}
|
| 209 |
const extension = filename.split('.').pop().toLowerCase();
|
| 210 |
let iconSvg = '';
|
| 211 |
switch (extension) {
|
|
|
|
| 216 |
}
|
| 217 |
return iconSvg;
|
| 218 |
}
|
| 219 |
+
|
| 220 |
+
function resetFileSelection() {
|
| 221 |
+
fileInput.value = '';
|
| 222 |
+
fileInfo.classList.add('hidden'); fileInfo.classList.remove('flex');
|
| 223 |
+
dropzone.classList.remove('hidden');
|
| 224 |
+
}
|
| 225 |
|
| 226 |
// --- Event Listeners & Core Logic ---
|
| 227 |
function handleFileSelect(file) {
|
| 228 |
if (!file) return;
|
| 229 |
+
youtubeUrlInput.value = ''; // Clear URL input if a file is selected
|
| 230 |
fileNameText.textContent = file.name;
|
| 231 |
fileSizeText.textContent = `${(file.size / 1024 / 1024).toFixed(2)} MB`;
|
| 232 |
fileIconContainer.innerHTML = getFileIcon(file.name);
|
|
|
|
| 245 |
fileInput.files = e.dataTransfer.files;
|
| 246 |
handleFileSelect(file);
|
| 247 |
});
|
| 248 |
+
removeFileBtn.addEventListener('click', resetFileSelection);
|
| 249 |
+
|
| 250 |
+
youtubeUrlInput.addEventListener('input', () => {
|
| 251 |
+
if (youtubeUrlInput.value.trim() !== '') {
|
| 252 |
+
resetFileSelection(); // Clear file selection if URL is being typed
|
| 253 |
+
}
|
| 254 |
});
|
| 255 |
|
| 256 |
uploadForm.addEventListener('submit', async (e) => {
|
| 257 |
e.preventDefault();
|
| 258 |
+
if (!fileInput.files[0] && !youtubeUrlInput.value.trim()) {
|
| 259 |
+
alert('Veuillez sélectionner un fichier ou entrer une URL YouTube.');
|
| 260 |
+
return;
|
| 261 |
+
}
|
| 262 |
showLoading(true);
|
| 263 |
+
resultCard.classList.add('hidden');
|
| 264 |
+
errorCard.classList.add('hidden');
|
| 265 |
+
|
| 266 |
const formData = new FormData(uploadForm);
|
| 267 |
+
|
| 268 |
try {
|
| 269 |
const response = await fetch('/upload', { method: 'POST', body: formData });
|
| 270 |
const data = await response.json();
|
| 271 |
if (!response.ok || data.error) throw new Error(data.error || `Erreur serveur: ${response.status}`);
|
| 272 |
+
|
| 273 |
+
let sourceName = fileInput.files[0] ? fileInput.files[0].name : 'Vidéo YouTube';
|
| 274 |
+
const newItem = { id: data.history_id, summary: data.summary, filename: sourceName };
|
| 275 |
displaySummary(newItem);
|
| 276 |
loadHistory();
|
| 277 |
+
|
| 278 |
} catch (err) {
|
| 279 |
errorMessage.textContent = err.message;
|
| 280 |
errorCard.classList.remove('hidden'); errorCard.classList.add('fade-in-up');
|
|
|
|
| 315 |
heightLeft -= pdfHeight;
|
| 316 |
}
|
| 317 |
|
| 318 |
+
const safeFilename = resultFilename.textContent.replace(/[^a-z0-9]/gi, '_').toLowerCase() + "_resume.pdf";
|
| 319 |
pdf.save(safeFilename);
|
| 320 |
|
| 321 |
} catch (err) {
|
|
|
|
| 325 |
downloadBtn.disabled = false;
|
| 326 |
downloadBtnText.textContent = 'Télécharger PDF';
|
| 327 |
downloadBtnIcon.classList.remove('hidden');
|
| 328 |
+
downloadBtnSpinner.add('hidden');
|
| 329 |
}
|
| 330 |
});
|
| 331 |
|