CORVO-AI commited on
Commit
cdadfa3
·
1 Parent(s): 5c37ebc

Upload index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +543 -0
templates/index.html ADDED
@@ -0,0 +1,543 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Book Extraction System</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ }
20
+
21
+ .container {
22
+ max-width: 800px;
23
+ margin: 0 auto;
24
+ }
25
+
26
+ .header {
27
+ background: white;
28
+ padding: 30px;
29
+ border-radius: 15px;
30
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
31
+ text-align: center;
32
+ margin-bottom: 30px;
33
+ }
34
+
35
+ .header h1 {
36
+ color: #667eea;
37
+ font-size: 2.5em;
38
+ margin-bottom: 10px;
39
+ }
40
+
41
+ .header p {
42
+ color: #666;
43
+ font-size: 1.1em;
44
+ }
45
+
46
+ .reconnect-banner {
47
+ background: #ff9800;
48
+ color: white;
49
+ padding: 15px;
50
+ border-radius: 10px;
51
+ margin-bottom: 20px;
52
+ text-align: center;
53
+ font-weight: 600;
54
+ display: none;
55
+ }
56
+
57
+ .reconnect-banner.show {
58
+ display: block;
59
+ }
60
+
61
+ .card {
62
+ background: white;
63
+ padding: 30px;
64
+ border-radius: 15px;
65
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
66
+ margin-bottom: 20px;
67
+ }
68
+
69
+ .input-group {
70
+ margin-bottom: 20px;
71
+ }
72
+
73
+ .input-group label {
74
+ display: block;
75
+ margin-bottom: 10px;
76
+ color: #333;
77
+ font-weight: 600;
78
+ font-size: 1.1em;
79
+ }
80
+
81
+ .input-group input {
82
+ width: 100%;
83
+ padding: 15px;
84
+ border: 2px solid #e0e0e0;
85
+ border-radius: 8px;
86
+ font-size: 1em;
87
+ transition: border-color 0.3s;
88
+ }
89
+
90
+ .input-group input:focus {
91
+ outline: none;
92
+ border-color: #667eea;
93
+ }
94
+
95
+ .btn {
96
+ width: 100%;
97
+ padding: 15px;
98
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
99
+ color: white;
100
+ border: none;
101
+ border-radius: 8px;
102
+ font-size: 1.2em;
103
+ font-weight: 600;
104
+ cursor: pointer;
105
+ transition: transform 0.2s, box-shadow 0.2s;
106
+ }
107
+
108
+ .btn:hover {
109
+ transform: translateY(-2px);
110
+ box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
111
+ }
112
+
113
+ .btn:active {
114
+ transform: translateY(0);
115
+ }
116
+
117
+ .btn:disabled {
118
+ background: #ccc;
119
+ cursor: not-allowed;
120
+ transform: none;
121
+ }
122
+
123
+ .progress-card {
124
+ display: none;
125
+ }
126
+
127
+ .progress-card.active {
128
+ display: block;
129
+ }
130
+
131
+ .progress-bar-container {
132
+ width: 100%;
133
+ height: 30px;
134
+ background: #e0e0e0;
135
+ border-radius: 15px;
136
+ overflow: hidden;
137
+ margin-bottom: 20px;
138
+ }
139
+
140
+ .progress-bar {
141
+ height: 100%;
142
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
143
+ width: 0%;
144
+ transition: width 0.5s;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ color: white;
149
+ font-weight: 600;
150
+ }
151
+
152
+ .status-box {
153
+ background: #f5f5f5;
154
+ padding: 20px;
155
+ border-radius: 8px;
156
+ margin-bottom: 20px;
157
+ }
158
+
159
+ .status-box h3 {
160
+ color: #667eea;
161
+ margin-bottom: 10px;
162
+ }
163
+
164
+ .status-box p {
165
+ color: #666;
166
+ line-height: 1.6;
167
+ }
168
+
169
+ .log-box {
170
+ background: #1e1e1e;
171
+ color: #00ff00;
172
+ padding: 20px;
173
+ border-radius: 8px;
174
+ font-family: 'Courier New', monospace;
175
+ font-size: 0.9em;
176
+ max-height: 300px;
177
+ overflow-y: auto;
178
+ margin-top: 20px;
179
+ }
180
+
181
+ .log-box p {
182
+ margin-bottom: 5px;
183
+ }
184
+
185
+ .folders-list {
186
+ background: #f5f5f5;
187
+ padding: 15px;
188
+ border-radius: 8px;
189
+ margin-top: 10px;
190
+ max-height: 200px;
191
+ overflow-y: auto;
192
+ }
193
+
194
+ .folder-item {
195
+ padding: 10px;
196
+ background: white;
197
+ margin-bottom: 5px;
198
+ border-radius: 5px;
199
+ cursor: pointer;
200
+ transition: background 0.2s;
201
+ }
202
+
203
+ .folder-item:hover {
204
+ background: #667eea;
205
+ color: white;
206
+ }
207
+
208
+ .status-idle { color: #999; }
209
+ .status-running { color: #667eea; }
210
+ .status-completed { color: #4caf50; }
211
+ .status-error { color: #f44336; }
212
+
213
+ .alert {
214
+ padding: 15px;
215
+ border-radius: 8px;
216
+ margin-bottom: 20px;
217
+ display: none;
218
+ }
219
+
220
+ .alert.show {
221
+ display: block;
222
+ }
223
+
224
+ .alert-success {
225
+ background: #d4edda;
226
+ color: #155724;
227
+ border: 1px solid #c3e6cb;
228
+ }
229
+
230
+ .alert-error {
231
+ background: #f8d7da;
232
+ color: #721c24;
233
+ border: 1px solid #f5c6cb;
234
+ }
235
+
236
+ .spinner {
237
+ display: inline-block;
238
+ width: 20px;
239
+ height: 20px;
240
+ border: 3px solid rgba(255,255,255,.3);
241
+ border-radius: 50%;
242
+ border-top-color: #fff;
243
+ animation: spin 1s ease-in-out infinite;
244
+ }
245
+
246
+ @keyframes spin {
247
+ to { transform: rotate(360deg); }
248
+ }
249
+ </style>
250
+ </head>
251
+ <body>
252
+ <div class="container">
253
+ <!-- Header -->
254
+ <div class="header">
255
+ <h1>📚 AI Book Extraction System</h1>
256
+ <p>Extract and explain book content using AI</p>
257
+ </div>
258
+
259
+ <!-- Reconnect Banner -->
260
+ <div id="reconnectBanner" class="reconnect-banner">
261
+ 🔄 Reconnected! Found active extraction in progress...
262
+ </div>
263
+
264
+ <!-- Alert Messages -->
265
+ <div id="alert" class="alert"></div>
266
+
267
+ <!-- Input Card -->
268
+ <div class="card" id="inputCard">
269
+ <div class="input-group">
270
+ <label for="folderName">📁 Enter Folder Name:</label>
271
+ <input
272
+ type="text"
273
+ id="folderName"
274
+ placeholder="e.g., math, physics, chemistry..."
275
+ autocomplete="off"
276
+ >
277
+ </div>
278
+
279
+ <button class="btn" id="startBtn" onclick="startExtraction()">
280
+ 🚀 Start Extraction
281
+ </button>
282
+
283
+ <div style="text-align: center; margin: 20px 0; color: #999;">
284
+ <p>OR</p>
285
+ </div>
286
+
287
+ <button class="btn" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);" onclick="loadFolders()">
288
+ 📂 Show Available Folders
289
+ </button>
290
+
291
+ <div id="foldersList" class="folders-list" style="display: none;"></div>
292
+ </div>
293
+
294
+ <!-- Progress Card -->
295
+ <div class="card progress-card" id="progressCard">
296
+ <h2 style="color: #667eea; margin-bottom: 20px;">⚙️ Processing...</h2>
297
+
298
+ <div class="progress-bar-container">
299
+ <div class="progress-bar" id="progressBar">0%</div>
300
+ </div>
301
+
302
+ <div class="status-box">
303
+ <h3>📊 Status</h3>
304
+ <p><strong>Folder:</strong> <span id="folderNameDisplay">-</span></p>
305
+ <p><strong>Current Page:</strong> <span id="currentPage">0</span> / <span id="totalPages">0</span></p>
306
+ <p><strong>Status:</strong> <span id="statusText" class="status-idle">Idle</span></p>
307
+ <p style="margin-top: 10px;"><strong>Message:</strong></p>
308
+ <p id="statusMessage" style="font-style: italic;">Waiting to start...</p>
309
+ </div>
310
+
311
+ <div class="log-box" id="logBox">
312
+ <p>System ready. Waiting for process to start...</p>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
317
+ <script>
318
+ let progressInterval = null;
319
+ let logMessages = [];
320
+ let hasCheckedStatus = false;
321
+
322
+ // Check for active extraction on page load
323
+ window.addEventListener('load', function() {
324
+ checkActiveExtraction();
325
+ });
326
+
327
+ function checkActiveExtraction() {
328
+ fetch('/check_status')
329
+ .then(response => response.json())
330
+ .then(data => {
331
+ if (data.has_active) {
332
+ // Show reconnect banner
333
+ document.getElementById('reconnectBanner').classList.add('show');
334
+ setTimeout(() => {
335
+ document.getElementById('reconnectBanner').classList.remove('show');
336
+ }, 5000);
337
+
338
+ // Hide input card and show progress card
339
+ document.getElementById('inputCard').style.display = 'none';
340
+ document.getElementById('progressCard').classList.add('active');
341
+
342
+ // Update folder name display
343
+ document.getElementById('folderNameDisplay').textContent = data.progress.folder_name || 'Unknown';
344
+
345
+ // Start tracking progress
346
+ startProgressTracking();
347
+
348
+ hasCheckedStatus = true;
349
+ }
350
+ })
351
+ .catch(error => {
352
+ console.error('Error checking status:', error);
353
+ });
354
+ }
355
+
356
+ function showAlert(message, type) {
357
+ const alert = document.getElementById('alert');
358
+ alert.className = `alert alert-${type} show`;
359
+ alert.textContent = message;
360
+
361
+ setTimeout(() => {
362
+ alert.classList.remove('show');
363
+ }, 5000);
364
+ }
365
+
366
+ function startExtraction() {
367
+ const folderName = document.getElementById('folderName').value.trim();
368
+
369
+ if (!folderName) {
370
+ showAlert('Please enter a folder name!', 'error');
371
+ return;
372
+ }
373
+
374
+ const startBtn = document.getElementById('startBtn');
375
+ startBtn.disabled = true;
376
+ startBtn.innerHTML = '<span class="spinner"></span> Starting...';
377
+
378
+ fetch('/start', {
379
+ method: 'POST',
380
+ headers: {
381
+ 'Content-Type': 'application/json',
382
+ },
383
+ body: JSON.stringify({ folder_name: folderName })
384
+ })
385
+ .then(response => response.json())
386
+ .then(data => {
387
+ if (data.success) {
388
+ showAlert(data.message, 'success');
389
+ document.getElementById('inputCard').style.display = 'none';
390
+ document.getElementById('progressCard').classList.add('active');
391
+ document.getElementById('folderNameDisplay').textContent = folderName;
392
+ startProgressTracking();
393
+ } else {
394
+ if (data.already_running) {
395
+ // Another extraction is running, show it
396
+ document.getElementById('inputCard').style.display = 'none';
397
+ document.getElementById('progressCard').classList.add('active');
398
+ startProgressTracking();
399
+ showAlert('Reconnected to active extraction!', 'success');
400
+ } else {
401
+ showAlert(data.message, 'error');
402
+ startBtn.disabled = false;
403
+ startBtn.innerHTML = '🚀 Start Extraction';
404
+ }
405
+ }
406
+ })
407
+ .catch(error => {
408
+ showAlert('Error: ' + error.message, 'error');
409
+ startBtn.disabled = false;
410
+ startBtn.innerHTML = '🚀 Start Extraction';
411
+ });
412
+ }
413
+
414
+ function startProgressTracking() {
415
+ logMessages = [];
416
+ if (progressInterval) {
417
+ clearInterval(progressInterval);
418
+ }
419
+ progressInterval = setInterval(updateProgress, 1000);
420
+ }
421
+
422
+ function updateProgress() {
423
+ fetch('/progress')
424
+ .then(response => response.json())
425
+ .then(data => {
426
+ const currentPage = data.current_page || 0;
427
+ const totalPages = data.total_pages || 0;
428
+ const status = data.status || 'idle';
429
+ const message = data.message || '';
430
+ const folderName = data.folder_name || '';
431
+
432
+ // Update folder name if available
433
+ if (folderName) {
434
+ document.getElementById('folderNameDisplay').textContent = folderName;
435
+ }
436
+
437
+ // Update progress bar
438
+ const percentage = totalPages > 0 ? Math.round((currentPage / totalPages) * 100) : 0;
439
+ document.getElementById('progressBar').style.width = percentage + '%';
440
+ document.getElementById('progressBar').textContent = percentage + '%';
441
+
442
+ // Update status
443
+ document.getElementById('currentPage').textContent = currentPage;
444
+ document.getElementById('totalPages').textContent = totalPages;
445
+
446
+ const statusText = document.getElementById('statusText');
447
+ statusText.textContent = status.toUpperCase();
448
+ statusText.className = 'status-' + status;
449
+
450
+ document.getElementById('statusMessage').textContent = message;
451
+
452
+ // Update log
453
+ if (message && !logMessages.includes(message)) {
454
+ logMessages.push(message);
455
+ const logBox = document.getElementById('logBox');
456
+ const p = document.createElement('p');
457
+ p.textContent = '> ' + message;
458
+ logBox.appendChild(p);
459
+ logBox.scrollTop = logBox.scrollHeight;
460
+ }
461
+
462
+ // Check if completed or error
463
+ if (status === 'completed') {
464
+ clearInterval(progressInterval);
465
+ showAlert('✅ Extraction completed successfully! File uploaded to GitHub.', 'success');
466
+ setTimeout(() => {
467
+ location.reload();
468
+ }, 5000);
469
+ } else if (status === 'error') {
470
+ clearInterval(progressInterval);
471
+ showAlert('❌ Extraction failed. Check the logs for details.', 'error');
472
+ }
473
+ })
474
+ .catch(error => {
475
+ console.error('Error fetching progress:', error);
476
+ });
477
+ }
478
+
479
+ function loadFolders() {
480
+ fetch('/folders')
481
+ .then(response => response.json())
482
+ .then(data => {
483
+ if (data.success) {
484
+ const foldersList = document.getElementById('foldersList');
485
+ foldersList.innerHTML = '';
486
+
487
+ if (data.folders.length === 0) {
488
+ foldersList.innerHTML = '<p style="text-align: center; color: #999;">No folders found</p>';
489
+ } else {
490
+ data.folders.forEach(folder => {
491
+ const div = document.createElement('div');
492
+ div.className = 'folder-item';
493
+ div.textContent = '📁 ' + folder;
494
+ div.onclick = () => {
495
+ document.getElementById('folderName').value = folder;
496
+ foldersList.style.display = 'none';
497
+ };
498
+ foldersList.appendChild(div);
499
+ });
500
+ }
501
+
502
+ foldersList.style.display = 'block';
503
+ } else {
504
+ showAlert('Error loading folders: ' + data.message, 'error');
505
+ }
506
+ })
507
+ .catch(error => {
508
+ showAlert('Error: ' + error.message, 'error');
509
+ });
510
+ }
511
+
512
+ // Allow Enter key to start
513
+ document.getElementById('folderName').addEventListener('keypress', function(e) {
514
+ if (e.key === 'Enter') {
515
+ startExtraction();
516
+ }
517
+ });
518
+
519
+ // Prevent page from showing input card if extraction is running
520
+ setInterval(() => {
521
+ if (!hasCheckedStatus) return;
522
+
523
+ fetch('/check_status')
524
+ .then(response => response.json())
525
+ .then(data => {
526
+ if (data.has_active) {
527
+ // Make sure progress card is showing
528
+ if (!document.getElementById('progressCard').classList.contains('active')) {
529
+ document.getElementById('inputCard').style.display = 'none';
530
+ document.getElementById('progressCard').classList.add('active');
531
+ if (!progressInterval) {
532
+ startProgressTracking();
533
+ }
534
+ }
535
+ }
536
+ })
537
+ .catch(error => {
538
+ console.error('Error in background check:', error);
539
+ });
540
+ }, 3000); // Check every 3 seconds
541
+ </script>
542
+ </body>
543
+ </html>