GOWREESH M G commited on
Commit
4cf9f23
·
verified ·
1 Parent(s): 7f50b2f

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +304 -135
templates/index.html CHANGED
@@ -69,6 +69,7 @@
69
  @keyframes floatBlob { 0% { transform: translate(0, 0) scale(1); } 100% { transform: translate(50px, 80px) scale(1.1); } }
70
 
71
  .glass-island { background: var(--glass-bg); backdrop-filter: blur(40px); border: 1px solid var(--glass-border); box-shadow: var(--glass-shadow); border-radius: 2.5rem; transition: all 0.5s; }
 
72
  .inner-btn { background: var(--btn-bg); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 2.2rem; padding: 1.6rem; display: flex; align-items: center; gap: 1.5rem; cursor: pointer; transition: all 0.4s; }
73
  .inner-btn:hover:not(.disabled) { transform: scale(1.04) translateY(-6px); background: rgba(255, 255, 255, 0.25); box-shadow: 0 15px 35px rgba(124, 58, 237, 0.15); }
74
  html.dark .inner-btn:hover:not(.disabled) { background: rgba(255, 255, 255, 0.12); box-shadow: 0 15px 35px rgba(217, 70, 239, 0.2); }
@@ -78,10 +79,24 @@
78
  .icon-vibrant { transition: all 0.4s ease; }
79
  .icon-purple { color: #6d28d9; }
80
  .icon-pink { color: #db2777; }
 
81
  html.dark .icon-purple { color: #a855f7; filter: drop-shadow(0 0 12px rgba(168, 85, 247, 0.6)); }
82
  html.dark .icon-pink { color: #f472b6; filter: drop-shadow(0 0 12px rgba(244, 114, 182, 0.6)); }
 
83
  .inner-btn:hover:not(.disabled) .icon-vibrant { transform: scale(1.2) rotate(-5deg); }
84
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  .status-badge { display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; background: rgba(255, 255, 255, 0.25); border: 1px solid rgba(255, 255, 255, 0.4); border-radius: 99px; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-secondary); backdrop-filter: blur(10px); }
86
  html.dark .status-badge { background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.15); }
87
  .status-dot { width: 8px; height: 8px; background: #22c55e; border-radius: 50%; box-shadow: 0 0 10px #22c55e; animation: pulse 2s infinite; }
@@ -107,7 +122,6 @@
107
 
108
  /* Modal Glass styles */
109
  .modal-glass { background: rgba(30, 30, 30, 0.45); backdrop-filter: blur(40px); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 2rem; box-shadow: 0 25px 60px rgba(0,0,0,0.5); }
110
-
111
  .img-magnifier-glass { position: absolute; border: 3px solid rgba(255,255,255,0.8); border-radius: 50%; cursor: none; width: 100px; height: 100px; display: none; box-shadow: 0 10px 20px rgba(0,0,0,0.5); pointer-events: none; z-index: 30; }
112
 
113
  @keyframes scan { 0% { top: 0%; opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { top: 100%; opacity: 0; } }
@@ -121,14 +135,17 @@
121
  * { text-shadow: none !important; }
122
  h2, p, span, div { color: black !important; }
123
 
 
 
 
 
124
  .print-header { display: block !important; text-align: center; margin-bottom: 25px; border-bottom: 2px solid #000; padding-bottom: 15px; }
125
  .print-header h1 { font-size: 22px; margin-bottom: 5px; font-weight: bold; font-family: 'Times New Roman', serif; }
126
  .print-header p { font-size: 12px; color: #555 !important; font-family: 'Times New Roman', serif; margin: 2px 0; }
127
 
128
  .result-image-frame { margin: 0 auto 20px auto !important; width: 220px !important; height: 220px !important; border: 2px solid #000 !important; border-radius: 10px !important; box-shadow: none !important; }
129
- #view-result { opacity: 1 !important; transform: none !important; display: block !important; max-width: 100% !important; }
130
- #result-icon-bg { border: 2px solid #000 !important; box-shadow: none !important; background: transparent !important; border-radius: 50% !important;}
131
- #result-icon-bg i { color: black !important; }
132
  .bg-black\/5, .bg-blue-500\/10 { background: white !important; border: 1px solid #aaa !important; }
133
 
134
  .print-details { display: block !important; margin-top: 25px; text-align: left; border-top: 2px solid #000; padding-top: 15px;}
@@ -139,10 +156,12 @@
139
  </style>
140
  </head>
141
  <body>
 
142
  <div id="initial-loader" class="fixed inset-0 z-[999] flex flex-col items-center justify-center">
143
  <div class="relative flex flex-col items-center">
144
  <div class="mb-4 relative">
145
  <div class="absolute inset-0 bg-purple-500 blur-2xl opacity-40 rounded-full animate-pulse"></div>
 
146
  <svg width="76" height="76" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg" class="relative z-10 drop-shadow-2xl">
147
  <defs>
148
  <linearGradient id="loadMainGradient" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#7c3aed"/><stop offset="100%" stop-color="#d946ef"/></linearGradient>
@@ -174,6 +193,7 @@
174
  </div>
175
  </div>
176
 
 
177
  <div class="bg-blob blob-1"></div><div class="bg-blob blob-2"></div>
178
 
179
  <button id="theme-toggle-btn" onclick="toggleTheme()" class="fixed top-6 right-6 z-50 p-2.5 rounded-full glass-island hover:scale-110 active:scale-95 transition-all shadow-lg backdrop-blur-md">
@@ -181,13 +201,21 @@
181
  </button>
182
 
183
  <main class="relative z-10 w-full min-h-screen flex flex-col items-center pt-20 pb-12 px-6">
 
184
  <!-- VIEW 1: HOME -->
185
  <div id="view-home" class="view-section active">
186
- <div class="text-center mb-10 flex flex-col items-center gap-6">
187
- <svg width="90" height="90" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg" class="drop-shadow-2xl hover:scale-105 transition-transform duration-500">
 
188
  <defs>
189
- <linearGradient id="mainGradient" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#7c3aed"/><stop offset="100%" stop-color="#d946ef"/></linearGradient>
190
- <linearGradient id="irisGradient" x1="256" y1="150" x2="256" y2="362" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#4c1d95"/><stop offset="100%" stop-color="#7c3aed"/></linearGradient>
 
 
 
 
 
 
191
  </defs>
192
  <rect x="32" y="32" width="448" height="448" rx="110" fill="url(#mainGradient)" />
193
  <path d="M256 160C176 160 110 210 80 256C110 302 176 352 256 352C336 352 402 302 432 256C402 210 336 160 256 160Z" fill="white"/>
@@ -196,24 +224,31 @@
196
  <circle cx="276" cy="236" r="10" fill="white" fill-opacity="0.9"/>
197
  <ellipse cx="256" cy="110" rx="160" ry="80" fill="white" fill-opacity="0.15"/>
198
  <g stroke="white" stroke-width="8" stroke-linecap="round" stroke-opacity="0.9">
199
- <path d="M140 140 L110 140 L110 170" fill="none"/><path d="M372 140 L402 140 L402 170" fill="none"/><path d="M140 372 L110 372 L110 342" fill="none"/><path d="M372 372 L402 372 L402 342" fill="none"/>
 
 
 
200
  </g>
201
  <rect x="110" y="254" width="292" height="4" rx="2" fill="white" fill-opacity="0.6"/>
202
  </svg>
 
203
  <div>
204
- <h1 class="text-5xl font-black mb-2"><span class="logo-shimmer">RetinaScan</span></h1>
205
- <div class="status-badge"><span class="status-dot"></span>AI Ensemble Active</div>
206
  </div>
207
  </div>
208
 
 
209
  <div class="glass-island p-8 flex flex-col gap-5 w-full max-w-[420px]" id="selection-island">
210
- <div onclick="startCameraFlow()" id="btn-camera" class="inner-btn group">
211
- <i data-lucide="camera" class="w-10 h-10 icon-vibrant icon-purple"></i>
212
- <div class="text-left"><span class="block text-xl font-bold">Take Photo</span><span class="sub-label opacity-70">PHONE CAMERA</span></div>
213
- </div>
214
  <div onclick="triggerUpload()" id="btn-upload" class="inner-btn group">
215
- <i data-lucide="image" class="w-10 h-10 icon-vibrant icon-pink"></i>
216
- <div class="text-left"><span class="block text-xl font-bold">Choose Photo</span><span class="sub-label opacity-70">Photo Library</span></div>
 
 
 
 
 
217
  </div>
218
  </div>
219
 
@@ -221,11 +256,13 @@
221
  <i data-lucide="lightbulb" class="w-5 h-5 text-yellow-500 mt-1 flex-shrink-0"></i>
222
  <div>
223
  <h3 class="font-bold text-sm mb-1">Quick Tip</h3>
224
- <p class="text-xs opacity-70 leading-relaxed">Ensure the room is well-lit and avoid glare on the screen for the most accurate AI diagnosis.</p>
225
  </div>
226
  </div>
227
- <footer class="mt-auto pt-8 text-center opacity-40 print-hide"><p class="text-[10px] font-mono uppercase tracking-widest">Powered by PyTorch Ensemble</p></footer>
228
 
 
 
 
229
  <div id="scanning-section" class="w-full max-w-[420px] glass-island p-8 flex flex-col items-center">
230
  <div class="relative w-56 h-56 rounded-[2rem] overflow-hidden shadow-2xl mb-6 bg-black/5 dark:bg-black/40 border border-black/10 dark:border-white/20">
231
  <img id="scan-preview" src="" class="w-full h-full object-cover opacity-90" />
@@ -240,7 +277,7 @@
240
  <input type="file" id="file-input" class="hidden" accept="image/*" onchange="handleFileSelect(event)" />
241
  </div>
242
 
243
- <!-- VIEW 2: RESULTS -->
244
  <div id="view-result" class="view-section">
245
  <div class="print-header">
246
  <h1>RETINASCAN MEDICAL DIAGNOSTICS</h1>
@@ -249,7 +286,6 @@
249
  </div>
250
 
251
  <div class="result-image-frame print-hide group relative" onclick="openImageModal()">
252
- <!-- Hover overlay icon indicating it's clickable -->
253
  <div class="absolute inset-0 bg-black/40 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity z-10 rounded-[1.6rem]">
254
  <i data-lucide="maximize-2" class="w-8 h-8 text-white"></i>
255
  <span class="text-white text-xs font-bold mt-2 uppercase tracking-widest drop-shadow-md">View Full Screen</span>
@@ -266,7 +302,6 @@
266
  <p id="result-severity" class="sub-label text-purple-600 dark:text-purple-400 mb-4 font-black text-xs print-hide">ANALYSIS COMPLETE</p>
267
  <p id="result-desc" class="text-sm opacity-70 mb-6 font-medium text-center leading-relaxed max-w-[260px] print-hide">--</p>
268
 
269
- <!-- Confidence Score (Hidden on Print) -->
270
  <div class="w-full bg-black/5 dark:bg-white/5 p-4 rounded-2xl border border-black/10 dark:border-white/10 text-center mb-4 shadow-inner print-hide">
271
  <p class="sub-label mb-1 opacity-50">AI Confidence Score</p>
272
  <p id="result-conf" class="text-2xl font-mono font-bold">--%</p>
@@ -292,7 +327,6 @@
292
  <p id="print-meds">---</p>
293
  </div>
294
 
295
- <!-- Sync'd Action Buttons (Main View) -->
296
  <div class="flex gap-3 w-full mb-3 print-hide">
297
  <button onclick="toggleHeatmap()" id="btn-main-heatmap" class="flex-[1] py-3.5 rounded-2xl bg-purple-500/10 hover:bg-purple-500/20 text-purple-700 dark:text-purple-300 font-bold text-sm border border-purple-500/20 transition-all hidden">
298
  <i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Heatmap
@@ -303,17 +337,149 @@
303
  </div>
304
 
305
  <button onclick="resetApp()" class="w-full py-4 rounded-2xl bg-purple-600 hover:bg-purple-700 text-white font-bold text-base shadow-xl shadow-purple-900/20 active:scale-95 transition-all print-hide">
306
- Start New Scan
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  </button>
308
  </div>
309
  </div>
310
 
311
- <!-- FULL SCREEN IMAGE MODAL (NEW) -->
312
  <div id="image-modal" class="hidden fixed inset-0 z-[200] flex items-center justify-center p-6">
313
  <div class="absolute inset-0 bg-black/80 backdrop-blur-xl" onclick="closeImageModal()"></div>
314
 
315
  <div class="relative w-full max-w-[420px] modal-glass p-6 flex flex-col items-center">
316
- <!-- Close Button -->
317
  <button onclick="closeImageModal()" class="absolute top-4 right-4 p-2.5 bg-white/10 hover:bg-white/20 rounded-full backdrop-blur text-white transition-all z-50">
318
  <i data-lucide="x" class="w-5 h-5"></i>
319
  </button>
@@ -322,45 +488,25 @@
322
  <h3 class="text-white font-bold text-lg tracking-wider">Image Inspection</h3>
323
  </div>
324
 
325
- <!-- Large Image Frame with Interactive Magnifier -->
326
  <div class="relative w-full aspect-square rounded-[1.5rem] overflow-hidden border-2 border-white/20 shadow-inner bg-black cursor-crosshair"
327
  onmouseenter="showModalMagnifier(true)"
328
  onmouseleave="showModalMagnifier(false)"
329
  onmousemove="moveModalMagnifier(event)"
330
  ontouchmove="moveModalMagnifier(event)">
331
-
332
  <div id="modal-magnifier-glass" class="img-magnifier-glass"></div>
333
-
334
  <img id="modal-photo" src="" class="w-full h-full object-cover transition-opacity duration-300" />
335
  </div>
336
 
337
- <!-- Heatmap Toggle Switch (Syncs with Main View) -->
338
  <button onclick="toggleHeatmap()" id="btn-modal-heatmap" class="mt-6 w-full py-4 rounded-2xl bg-purple-500/20 hover:bg-purple-500/30 text-white font-bold text-sm border border-purple-500/30 transition-all hidden shadow-lg shadow-purple-500/10">
339
  <i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Toggle AI Heatmap
340
  </button>
341
  </div>
342
  </div>
343
 
344
- <!-- VIEW 3: CAMERA -->
345
- <div id="view-camera" class="hidden fixed inset-0 bg-black z-[100] flex-col items-center justify-center">
346
- <video id="camera-feed" autoplay playsinline class="absolute inset-0 w-full h-full object-cover opacity-80"></video>
347
- <div class="absolute top-0 left-0 w-full p-8 flex justify-between items-start z-20">
348
- <button onclick="stopCamera()" class="p-4 rounded-full bg-black/40 text-white backdrop-blur-md hover:bg-black/60"><i data-lucide="x" class="w-6 h-6"></i></button>
349
- <button onclick="toggleTorch()" id="btn-torch" class="p-4 rounded-full bg-black/40 text-white backdrop-blur-md hover:bg-black/60 hidden"><i data-lucide="zap" class="w-6 h-6"></i></button>
350
- </div>
351
- <div class="relative z-10 w-72 h-72 rounded-full border-2 border-white/40 border-dashed shadow-[0_0_0_9999px_rgba(0,0,0,0.9)]"></div>
352
- <div class="absolute bottom-40 z-20 flex flex-col items-center gap-2 text-center w-full px-6">
353
- <p class="text-white font-bold text-lg tracking-wide drop-shadow-md">Align Eye in Circle</p>
354
- <p class="text-white/70 text-xs font-medium max-w-[200px] bg-black/30 px-3 py-1 rounded-full backdrop-blur-sm">Ensure good lighting & no glare</p>
355
- </div>
356
- <div class="absolute bottom-12 flex justify-center w-full z-20">
357
- <button onclick="capturePhoto()" class="w-20 h-20 rounded-full border-[4px] border-white flex items-center justify-center active:scale-90 hover:bg-white/10"><div class="w-16 h-16 rounded-full bg-white shadow-lg"></div></button>
358
- </div>
359
- <canvas id="camera-canvas" class="hidden"></canvas>
360
- </div>
361
  </main>
362
 
363
  <script>
 
364
  window.addEventListener('DOMContentLoaded', () => {
365
  const isDark = document.documentElement.classList.contains('dark');
366
  const metaTheme = document.getElementById('theme-color-meta');
@@ -397,18 +543,120 @@
397
  setTimeout(() => { if(!v.classList.contains('active')) v.style.display = 'none'; }, 500);
398
  });
399
  const target = document.getElementById(id);
400
- if(id !== 'view-camera') {
401
- document.getElementById('view-camera').classList.add('hidden');
402
- document.getElementById('view-camera').classList.remove('flex');
403
- document.getElementById('theme-toggle-btn').style.display = 'block';
404
- setTimeout(() => { target.style.display = 'flex'; void target.offsetWidth; target.classList.add('active'); }, 500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  }
407
 
408
  function resetApp() {
409
- stopCamera();
410
  document.getElementById('file-input').value = '';
411
- document.getElementById('btn-camera').classList.remove('disabled');
412
  document.getElementById('btn-upload').classList.remove('disabled');
413
  document.getElementById('scanning-section').classList.remove('show');
414
  setTimeout(() => {
@@ -419,58 +667,7 @@
419
  switchView('view-home');
420
  }
421
 
422
- let originalImageSrc = '';
423
- let heatmapImageSrc = '';
424
- let isHeatmapShowing = false;
425
- let stream = null; let track = null; let torchState = false;
426
-
427
- async function startCameraFlow() {
428
- if(document.getElementById('btn-camera').classList.contains('disabled')) return;
429
- const camView = document.getElementById('view-camera');
430
- camView.classList.remove('hidden'); camView.classList.add('flex');
431
- document.getElementById('theme-toggle-btn').style.display = 'none';
432
- try {
433
- stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', width: { ideal: 1920 }, height: { ideal: 1080 } } });
434
- const videoEl = document.getElementById('camera-feed');
435
- videoEl.srcObject = stream; track = stream.getVideoTracks()[0];
436
- if (track.getCapabilities().torch) {
437
- const torchBtn = document.getElementById('btn-torch');
438
- torchBtn.classList.remove('hidden'); torchState = false;
439
- }
440
- } catch(e) {
441
- alert("Could not access camera. Please check permissions.");
442
- camView.classList.add('hidden'); document.getElementById('theme-toggle-btn').style.display = 'block';
443
- }
444
- }
445
-
446
- async function toggleTorch() {
447
- if (!track) return;
448
- try {
449
- torchState = !torchState;
450
- await track.applyConstraints({ advanced: [{ torch: torchState }] });
451
- const btn = document.getElementById('btn-torch');
452
- if (torchState) { btn.classList.add('bg-yellow-500/80', 'text-black'); btn.classList.remove('bg-black/40', 'text-white'); }
453
- else { btn.classList.remove('bg-yellow-500/80', 'text-black'); btn.classList.add('bg-black/40', 'text-white'); }
454
- } catch (e) { torchState = !torchState; }
455
- }
456
-
457
- function stopCamera() {
458
- if (track && torchState) {
459
- track.applyConstraints({ advanced: [{ torch: false }] }).catch(() => {}); torchState = false;
460
- const btn = document.getElementById('btn-torch');
461
- btn.classList.remove('bg-yellow-500/80', 'text-black'); btn.classList.add('bg-black/40', 'text-white'); btn.classList.add('hidden');
462
- }
463
- if(stream) { stream.getTracks().forEach(t => t.stop()); stream = null; track = null; }
464
- document.getElementById('view-camera').classList.add('hidden'); document.getElementById('view-camera').classList.remove('flex');
465
- document.getElementById('theme-toggle-btn').style.display = 'block';
466
- }
467
-
468
- function capturePhoto() {
469
- const video = document.getElementById('camera-feed'); const canvas = document.getElementById('camera-canvas');
470
- canvas.width = video.videoWidth; canvas.height = video.videoHeight;
471
- const ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
472
- canvas.toBlob(blob => { stopCamera(); processImage(blob); }, 'image/jpeg', 0.95);
473
- }
474
 
475
  function triggerUpload() {
476
  if(document.getElementById('btn-upload').classList.contains('disabled')) return;
@@ -482,17 +679,13 @@
482
  function processImage(fileBlob) {
483
  const reader = new FileReader();
484
  reader.onload = (e) => {
485
- document.getElementById('btn-camera').classList.add('disabled');
486
  document.getElementById('btn-upload').classList.add('disabled');
487
-
488
  originalImageSrc = e.target.result;
489
  document.getElementById('scan-preview').src = originalImageSrc;
490
  document.getElementById('result-photo').src = originalImageSrc;
491
-
492
  const scanSection = document.getElementById('scanning-section');
493
  scanSection.style.display = 'flex';
494
  setTimeout(() => { scanSection.classList.add('show'); scanSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50);
495
-
496
  simulateScanProgress();
497
 
498
  const formData = new FormData();
@@ -501,7 +694,7 @@
501
  fetch('/analyze', { method: 'POST', body: formData })
502
  .then(r => r.json())
503
  .then(data => setTimeout(() => showResult(data), 3200))
504
- .catch(err => { alert("Analysis failed."); resetApp(); });
505
  };
506
  reader.readAsDataURL(fileBlob);
507
  }
@@ -517,7 +710,6 @@
517
  steps.forEach((step, i) => { setTimeout(() => { status.innerText = step.s; detail.innerText = step.d; bar.style.width = step.w; }, i * 800); });
518
  }
519
 
520
- // Updated Database with Detailed Evidence and Stage Information for Reports
521
  const medicalDatabase = {
522
  "No DR": {
523
  desc: "STAGE: NORMAL (No Diabetic Retinopathy).\n\nVISUAL EVIDENCE: The AI identified a healthy vascular structure. No microaneurysms, hemorrhages, or hard exudates were detected in the provided retinal image.",
@@ -582,31 +774,21 @@
582
  switchView('view-result');
583
  }
584
 
585
- // --- UNIFIED HEATMAP TOGGLE (Syncs Main View and Modal) ---
586
  function toggleHeatmap() {
587
  isHeatmapShowing = !isHeatmapShowing;
588
-
589
  const mainImg = document.getElementById('result-photo');
590
  const modalImg = document.getElementById('modal-photo');
591
  const mainBtn = document.getElementById('btn-main-heatmap');
592
  const modalBtn = document.getElementById('btn-modal-heatmap');
593
-
594
  const targetSrc = isHeatmapShowing ? heatmapImageSrc : originalImageSrc;
595
-
596
- // Fade out
597
  mainImg.style.opacity = '0';
598
  modalImg.style.opacity = '0';
599
 
600
  setTimeout(() => {
601
- // Change source
602
  mainImg.src = targetSrc;
603
  modalImg.src = targetSrc;
604
-
605
- // Fade in
606
  mainImg.style.opacity = '1';
607
  modalImg.style.opacity = '1';
608
-
609
- // Ensure magnifier updates its background if modal is open
610
  setTimeout(() => {
611
  if(!document.getElementById('image-modal').classList.contains('hidden')) {
612
  setupModalMagnifier();
@@ -615,22 +797,16 @@
615
  }, 300);
616
 
617
  if(isHeatmapShowing) {
618
- // Main Btn UI
619
  mainBtn.classList.add('bg-purple-500', 'text-white');
620
  mainBtn.classList.remove('bg-purple-500/10', 'text-purple-700', 'dark:text-purple-300');
621
  mainBtn.innerHTML = `<i data-lucide="eye-off" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Original`;
622
-
623
- // Modal Btn UI
624
  modalBtn.classList.add('bg-purple-500', 'text-white');
625
  modalBtn.classList.remove('bg-purple-500/20', 'text-white');
626
  modalBtn.innerHTML = `<i data-lucide="eye-off" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Hide AI Heatmap`;
627
  } else {
628
- // Main Btn UI
629
  mainBtn.classList.remove('bg-purple-500', 'text-white');
630
  mainBtn.classList.add('bg-purple-500/10', 'text-purple-700', 'dark:text-purple-300');
631
  mainBtn.innerHTML = `<i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Heatmap`;
632
-
633
- // Modal Btn UI
634
  modalBtn.classList.remove('bg-purple-500', 'text-white');
635
  modalBtn.classList.add('bg-purple-500/20', 'text-white');
636
  modalBtn.innerHTML = `<i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Toggle AI Heatmap`;
@@ -638,14 +814,11 @@
638
  lucide.createIcons();
639
  }
640
 
641
- // --- FULL SCREEN MODAL & MAGNIFIER LOGIC ---
642
-
643
  function openImageModal() {
644
  if(window.matchMedia('print').matches) return;
645
  const modal = document.getElementById('image-modal');
646
  const modalImg = document.getElementById('modal-photo');
647
  modalImg.src = isHeatmapShowing ? heatmapImageSrc : originalImageSrc;
648
-
649
  modal.classList.remove('hidden');
650
  setTimeout(setupModalMagnifier, 150);
651
  }
@@ -658,7 +831,6 @@
658
  function setupModalMagnifier() {
659
  const img = document.getElementById('modal-photo');
660
  const glass = document.getElementById('modal-magnifier-glass');
661
-
662
  glass.style.backgroundImage = "url('" + img.src + "')";
663
  glass.style.backgroundRepeat = "no-repeat";
664
  glass.style.backgroundSize = (img.clientWidth * zoomLevel) + "px " + (img.clientHeight * zoomLevel) + "px";
@@ -674,15 +846,12 @@
674
  const glass = document.getElementById('modal-magnifier-glass');
675
  const pos = getCursorPos(e, img);
676
  let x = pos.x; let y = pos.y;
677
-
678
  let w = glass.offsetWidth / 2; let h = glass.offsetHeight / 2;
679
  let bw = 3;
680
-
681
  if (x > img.clientWidth) { x = img.clientWidth; }
682
  if (x < 0) { x = 0; }
683
  if (y > img.clientHeight) { y = img.clientHeight; }
684
  if (y < 0) { y = 0; }
685
-
686
  glass.style.left = (x - w) + "px";
687
  glass.style.top = (y - h) + "px";
688
  glass.style.backgroundPosition = "-" + ((x * zoomLevel) - w + bw) + "px -" + ((y * zoomLevel) - h + bw) + "px";
 
69
  @keyframes floatBlob { 0% { transform: translate(0, 0) scale(1); } 100% { transform: translate(50px, 80px) scale(1.1); } }
70
 
71
  .glass-island { background: var(--glass-bg); backdrop-filter: blur(40px); border: 1px solid var(--glass-border); box-shadow: var(--glass-shadow); border-radius: 2.5rem; transition: all 0.5s; }
72
+
73
  .inner-btn { background: var(--btn-bg); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 2.2rem; padding: 1.6rem; display: flex; align-items: center; gap: 1.5rem; cursor: pointer; transition: all 0.4s; }
74
  .inner-btn:hover:not(.disabled) { transform: scale(1.04) translateY(-6px); background: rgba(255, 255, 255, 0.25); box-shadow: 0 15px 35px rgba(124, 58, 237, 0.15); }
75
  html.dark .inner-btn:hover:not(.disabled) { background: rgba(255, 255, 255, 0.12); box-shadow: 0 15px 35px rgba(217, 70, 239, 0.2); }
 
79
  .icon-vibrant { transition: all 0.4s ease; }
80
  .icon-purple { color: #6d28d9; }
81
  .icon-pink { color: #db2777; }
82
+ .icon-blue { color: #2563eb; }
83
  html.dark .icon-purple { color: #a855f7; filter: drop-shadow(0 0 12px rgba(168, 85, 247, 0.6)); }
84
  html.dark .icon-pink { color: #f472b6; filter: drop-shadow(0 0 12px rgba(244, 114, 182, 0.6)); }
85
+ html.dark .icon-blue { color: #60a5fa; filter: drop-shadow(0 0 12px rgba(96, 165, 250, 0.6)); }
86
  .inner-btn:hover:not(.disabled) .icon-vibrant { transform: scale(1.2) rotate(-5deg); }
87
 
88
+ /* FORM INPUT STYLES */
89
+ .glass-input {
90
+ width: 100%; padding: 0.9rem 1.2rem; border-radius: 1.2rem; margin-top: 0.3rem;
91
+ background: rgba(0,0,0,0.03); border: 1px solid rgba(0,0,0,0.1);
92
+ color: var(--text-main); font-weight: 600; font-size: 0.95rem; outline: none; transition: all 0.3s;
93
+ -webkit-appearance: none;
94
+ }
95
+ html.dark .glass-input { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: white; }
96
+ .glass-input:focus { border-color: #a855f7; box-shadow: 0 0 0 3px rgba(168,85,247,0.2); background: rgba(255,255,255,0.5); }
97
+ html.dark .glass-input:focus { background: rgba(0,0,0,0.2); }
98
+ .glass-input option { color: black; }
99
+
100
  .status-badge { display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; background: rgba(255, 255, 255, 0.25); border: 1px solid rgba(255, 255, 255, 0.4); border-radius: 99px; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-secondary); backdrop-filter: blur(10px); }
101
  html.dark .status-badge { background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.15); }
102
  .status-dot { width: 8px; height: 8px; background: #22c55e; border-radius: 50%; box-shadow: 0 0 10px #22c55e; animation: pulse 2s infinite; }
 
122
 
123
  /* Modal Glass styles */
124
  .modal-glass { background: rgba(30, 30, 30, 0.45); backdrop-filter: blur(40px); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 2rem; box-shadow: 0 25px 60px rgba(0,0,0,0.5); }
 
125
  .img-magnifier-glass { position: absolute; border: 3px solid rgba(255,255,255,0.8); border-radius: 50%; cursor: none; width: 100px; height: 100px; display: none; box-shadow: 0 10px 20px rgba(0,0,0,0.5); pointer-events: none; z-index: 30; }
126
 
127
  @keyframes scan { 0% { top: 0%; opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { top: 100%; opacity: 0; } }
 
135
  * { text-shadow: none !important; }
136
  h2, p, span, div { color: black !important; }
137
 
138
+ /* Logic to exclusively print the active view */
139
+ .view-section { display: none !important; }
140
+ .view-section.active { display: block !important; opacity: 1 !important; transform: none !important; max-width: 100% !important; }
141
+
142
  .print-header { display: block !important; text-align: center; margin-bottom: 25px; border-bottom: 2px solid #000; padding-bottom: 15px; }
143
  .print-header h1 { font-size: 22px; margin-bottom: 5px; font-weight: bold; font-family: 'Times New Roman', serif; }
144
  .print-header p { font-size: 12px; color: #555 !important; font-family: 'Times New Roman', serif; margin: 2px 0; }
145
 
146
  .result-image-frame { margin: 0 auto 20px auto !important; width: 220px !important; height: 220px !important; border: 2px solid #000 !important; border-radius: 10px !important; box-shadow: none !important; }
147
+ #result-icon-bg, #calc-icon-bg { border: 2px solid #000 !important; box-shadow: none !important; background: transparent !important; border-radius: 50% !important;}
148
+ #result-icon-bg i, #calc-icon-bg i { color: black !important; }
 
149
  .bg-black\/5, .bg-blue-500\/10 { background: white !important; border: 1px solid #aaa !important; }
150
 
151
  .print-details { display: block !important; margin-top: 25px; text-align: left; border-top: 2px solid #000; padding-top: 15px;}
 
156
  </style>
157
  </head>
158
  <body>
159
+ <!-- INITIAL LOADING SCREEN -->
160
  <div id="initial-loader" class="fixed inset-0 z-[999] flex flex-col items-center justify-center">
161
  <div class="relative flex flex-col items-center">
162
  <div class="mb-4 relative">
163
  <div class="absolute inset-0 bg-purple-500 blur-2xl opacity-40 rounded-full animate-pulse"></div>
164
+ <!-- SVG LOGO: Integrated from component -->
165
  <svg width="76" height="76" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg" class="relative z-10 drop-shadow-2xl">
166
  <defs>
167
  <linearGradient id="loadMainGradient" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#7c3aed"/><stop offset="100%" stop-color="#d946ef"/></linearGradient>
 
193
  </div>
194
  </div>
195
 
196
+ <!-- BACKGROUND BLOBS -->
197
  <div class="bg-blob blob-1"></div><div class="bg-blob blob-2"></div>
198
 
199
  <button id="theme-toggle-btn" onclick="toggleTheme()" class="fixed top-6 right-6 z-50 p-2.5 rounded-full glass-island hover:scale-110 active:scale-95 transition-all shadow-lg backdrop-blur-md">
 
201
  </button>
202
 
203
  <main class="relative z-10 w-full min-h-screen flex flex-col items-center pt-20 pb-12 px-6">
204
+
205
  <!-- VIEW 1: HOME -->
206
  <div id="view-home" class="view-section active">
207
+ <div class="text-center mb-8 flex flex-col items-center gap-4">
208
+ <!-- SVG LOGO: Restored to Home View -->
209
+ <svg width="80" height="80" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg" class="drop-shadow-2xl hover:scale-105 transition-transform duration-500">
210
  <defs>
211
+ <linearGradient id="mainGradient" x1="0" y1="0" x2="512" y2="512" gradientUnits="userSpaceOnUse">
212
+ <stop offset="0%" stop-color="#7c3aed"/>
213
+ <stop offset="100%" stop-color="#d946ef"/>
214
+ </linearGradient>
215
+ <linearGradient id="irisGradient" x1="256" y1="150" x2="256" y2="362" gradientUnits="userSpaceOnUse">
216
+ <stop offset="0%" stop-color="#4c1d95"/>
217
+ <stop offset="100%" stop-color="#7c3aed"/>
218
+ </linearGradient>
219
  </defs>
220
  <rect x="32" y="32" width="448" height="448" rx="110" fill="url(#mainGradient)" />
221
  <path d="M256 160C176 160 110 210 80 256C110 302 176 352 256 352C336 352 402 302 432 256C402 210 336 160 256 160Z" fill="white"/>
 
224
  <circle cx="276" cy="236" r="10" fill="white" fill-opacity="0.9"/>
225
  <ellipse cx="256" cy="110" rx="160" ry="80" fill="white" fill-opacity="0.15"/>
226
  <g stroke="white" stroke-width="8" stroke-linecap="round" stroke-opacity="0.9">
227
+ <path d="M140 140 L110 140 L110 170" fill="none"/>
228
+ <path d="M372 140 L402 140 L402 170" fill="none"/>
229
+ <path d="M140 372 L110 372 L110 342" fill="none"/>
230
+ <path d="M372 372 L402 372 L402 342" fill="none"/>
231
  </g>
232
  <rect x="110" y="254" width="292" height="4" rx="2" fill="white" fill-opacity="0.6"/>
233
  </svg>
234
+
235
  <div>
236
+ <h1 class="text-4xl font-black mb-2"><span class="logo-shimmer">RetinaScan</span></h1>
237
+ <div class="status-badge"><span class="status-dot"></span>Multi-Model AI</div>
238
  </div>
239
  </div>
240
 
241
+ <!-- SELECTION ISLAND -->
242
  <div class="glass-island p-8 flex flex-col gap-5 w-full max-w-[420px]" id="selection-island">
243
+
 
 
 
244
  <div onclick="triggerUpload()" id="btn-upload" class="inner-btn group">
245
+ <i data-lucide="image" class="w-8 h-8 icon-vibrant icon-pink"></i>
246
+ <div class="text-left"><span class="block text-xl font-bold">Upload Scan</span><span class="sub-label opacity-70">FUNDUS LIBRARY</span></div>
247
+ </div>
248
+
249
+ <div onclick="switchView('view-calculator')" id="btn-calculator" class="inner-btn group">
250
+ <i data-lucide="clipboard-list" class="w-8 h-8 icon-vibrant icon-blue"></i>
251
+ <div class="text-left"><span class="block text-xl font-bold">Clinical Risk Form</span><span class="sub-label opacity-70">DIABETES PREDICTOR</span></div>
252
  </div>
253
  </div>
254
 
 
256
  <i data-lucide="lightbulb" class="w-5 h-5 text-yellow-500 mt-1 flex-shrink-0"></i>
257
  <div>
258
  <h3 class="font-bold text-sm mb-1">Quick Tip</h3>
259
+ <p class="text-xs opacity-70 leading-relaxed">Upload a clear, glare-free image for accurate retinal analysis, or use the clinical form to instantly calculate your diabetes risk.</p>
260
  </div>
261
  </div>
 
262
 
263
+ <footer class="mt-auto pt-8 text-center opacity-40 print-hide"><p class="text-[10px] font-mono uppercase tracking-widest">Powered by PyTorch & Random Forest</p></footer>
264
+
265
+ <!-- SCAN LOADER -->
266
  <div id="scanning-section" class="w-full max-w-[420px] glass-island p-8 flex flex-col items-center">
267
  <div class="relative w-56 h-56 rounded-[2rem] overflow-hidden shadow-2xl mb-6 bg-black/5 dark:bg-black/40 border border-black/10 dark:border-white/20">
268
  <img id="scan-preview" src="" class="w-full h-full object-cover opacity-90" />
 
277
  <input type="file" id="file-input" class="hidden" accept="image/*" onchange="handleFileSelect(event)" />
278
  </div>
279
 
280
+ <!-- VIEW 2: RETINA RESULTS -->
281
  <div id="view-result" class="view-section">
282
  <div class="print-header">
283
  <h1>RETINASCAN MEDICAL DIAGNOSTICS</h1>
 
286
  </div>
287
 
288
  <div class="result-image-frame print-hide group relative" onclick="openImageModal()">
 
289
  <div class="absolute inset-0 bg-black/40 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity z-10 rounded-[1.6rem]">
290
  <i data-lucide="maximize-2" class="w-8 h-8 text-white"></i>
291
  <span class="text-white text-xs font-bold mt-2 uppercase tracking-widest drop-shadow-md">View Full Screen</span>
 
302
  <p id="result-severity" class="sub-label text-purple-600 dark:text-purple-400 mb-4 font-black text-xs print-hide">ANALYSIS COMPLETE</p>
303
  <p id="result-desc" class="text-sm opacity-70 mb-6 font-medium text-center leading-relaxed max-w-[260px] print-hide">--</p>
304
 
 
305
  <div class="w-full bg-black/5 dark:bg-white/5 p-4 rounded-2xl border border-black/10 dark:border-white/10 text-center mb-4 shadow-inner print-hide">
306
  <p class="sub-label mb-1 opacity-50">AI Confidence Score</p>
307
  <p id="result-conf" class="text-2xl font-mono font-bold">--%</p>
 
327
  <p id="print-meds">---</p>
328
  </div>
329
 
 
330
  <div class="flex gap-3 w-full mb-3 print-hide">
331
  <button onclick="toggleHeatmap()" id="btn-main-heatmap" class="flex-[1] py-3.5 rounded-2xl bg-purple-500/10 hover:bg-purple-500/20 text-purple-700 dark:text-purple-300 font-bold text-sm border border-purple-500/20 transition-all hidden">
332
  <i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Heatmap
 
337
  </div>
338
 
339
  <button onclick="resetApp()" class="w-full py-4 rounded-2xl bg-purple-600 hover:bg-purple-700 text-white font-bold text-base shadow-xl shadow-purple-900/20 active:scale-95 transition-all print-hide">
340
+ Done
341
+ </button>
342
+ </div>
343
+ </div>
344
+
345
+ <!-- VIEW 4: DIABETES CALCULATOR FORM -->
346
+ <div id="view-calculator" class="view-section">
347
+ <div class="w-full max-w-[420px] flex items-center justify-between mb-6">
348
+ <button onclick="switchView('view-home')" class="p-3 rounded-full bg-white/20 dark:bg-white/5 hover:bg-white/40 border border-white/20 transition-all">
349
+ <i data-lucide="arrow-left" class="w-5 h-5"></i>
350
+ </button>
351
+ <h2 class="text-xl font-bold tracking-tight">Clinical Risk Form</h2>
352
+ <div class="w-11"></div> <!-- spacer -->
353
+ </div>
354
+
355
+ <div class="glass-island p-8 w-full max-w-[420px]">
356
+ <form id="diabetes-form" onsubmit="submitDiabetesForm(event)" class="flex flex-col gap-5">
357
+
358
+ <div class="grid grid-cols-2 gap-4">
359
+ <div>
360
+ <label class="block sub-label mb-1">Gender</label>
361
+ <select id="calc-gender" required class="glass-input">
362
+ <option value="" disabled selected>Select</option>
363
+ <option value="Male">Male</option>
364
+ <option value="Female">Female</option>
365
+ <option value="Other">Other</option>
366
+ </select>
367
+ </div>
368
+ <div>
369
+ <label class="block sub-label mb-1">Age</label>
370
+ <input type="number" id="calc-age" required min="1" max="120" step="1" class="glass-input" placeholder="e.g. 45">
371
+ </div>
372
+ </div>
373
+
374
+ <div class="grid grid-cols-2 gap-4">
375
+ <div>
376
+ <label class="block sub-label mb-1">Hypertension</label>
377
+ <select id="calc-hyper" required class="glass-input">
378
+ <option value="" disabled selected>Select</option>
379
+ <option value="0">No</option>
380
+ <option value="1">Yes</option>
381
+ </select>
382
+ </div>
383
+ <div>
384
+ <label class="block sub-label mb-1">Heart Disease</label>
385
+ <select id="calc-heart" required class="glass-input">
386
+ <option value="" disabled selected>Select</option>
387
+ <option value="0">No</option>
388
+ <option value="1">Yes</option>
389
+ </select>
390
+ </div>
391
+ </div>
392
+
393
+ <div class="grid grid-cols-2 gap-4">
394
+ <div>
395
+ <label class="block sub-label mb-1">Smoking History</label>
396
+ <select id="calc-smoke" required class="glass-input">
397
+ <option value="" disabled selected>Select</option>
398
+ <option value="never">Never</option>
399
+ <option value="former">Former</option>
400
+ <option value="current">Current</option>
401
+ <option value="ever">Ever</option>
402
+ <option value="not current">Not Current</option>
403
+ <option value="No Info">No Info</option>
404
+ </select>
405
+ </div>
406
+ <div>
407
+ <label class="block sub-label mb-1">BMI</label>
408
+ <input type="number" id="calc-bmi" required min="10" max="80" step="0.01" class="glass-input" placeholder="e.g. 27.50">
409
+ </div>
410
+ </div>
411
+
412
+ <div class="grid grid-cols-2 gap-4">
413
+ <div>
414
+ <label class="block sub-label mb-1">HbA1c Level (%)</label>
415
+ <input type="number" id="calc-hba1c" required min="3" max="15" step="0.1" class="glass-input" placeholder="e.g. 6.5">
416
+ </div>
417
+ <div>
418
+ <label class="block sub-label mb-1">Blood Glucose</label>
419
+ <input type="number" id="calc-glucose" required min="50" max="400" step="1" class="glass-input" placeholder="e.g. 140">
420
+ </div>
421
+ </div>
422
+
423
+ <button type="submit" id="btn-submit-calc" class="mt-4 w-full py-4 rounded-2xl bg-blue-600 hover:bg-blue-700 text-white font-bold text-base shadow-xl active:scale-95 transition-all flex justify-center items-center gap-2">
424
+ <span>Calculate Risk</span>
425
+ <i data-lucide="calculator" class="w-5 h-5"></i>
426
+ </button>
427
+ </form>
428
+ </div>
429
+ </div>
430
+
431
+ <!-- VIEW 5: CALCULATOR RESULT -->
432
+ <div id="view-calc-result" class="view-section">
433
+
434
+ <!-- CALCULATOR PRINT HEADER -->
435
+ <div class="print-header">
436
+ <h1>CLINICAL DIABETES RISK REPORT</h1>
437
+ <p><strong>Patient ID:</strong> <span id="calc-print-id">---</span></p>
438
+ <p><strong>Date of Analysis:</strong> <span id="calc-print-date">---</span></p>
439
+ </div>
440
+
441
+ <div class="glass-island p-8 pt-10 w-full max-w-[420px] flex flex-col items-center">
442
+ <div id="calc-icon-bg" class="w-20 h-20 rounded-full flex items-center justify-center mb-6 shadow-xl transition-colors duration-500">
443
+ <i id="calc-icon" data-lucide="activity" class="w-10 h-10 text-white"></i>
444
+ </div>
445
+
446
+ <h2 id="calc-diagnosis" class="text-3xl font-bold mb-2 text-center tracking-tight">--</h2>
447
+ <p id="calc-severity" class="sub-label text-blue-600 dark:text-blue-400 mb-6 font-black text-xs print-hide">--</p>
448
+ <p id="calc-desc" class="text-sm opacity-80 mb-8 font-medium text-center leading-relaxed print-hide">--</p>
449
+
450
+ <!-- DIABETES DETAILED PRINT SECTION -->
451
+ <div class="print-details w-full" id="calc-print-medical-details">
452
+ <h3>Biomarker Analysis</h3>
453
+ <p id="calc-print-analysis">---</p>
454
+
455
+ <h3>Dietary Recommendations</h3>
456
+ <p id="calc-print-foods">---</p>
457
+
458
+ <h3>Lifestyle & Maintenance</h3>
459
+ <p id="calc-print-lifestyle">---</p>
460
+
461
+ <h3>Medical Interventions & Medicines</h3>
462
+ <p id="calc-print-meds">---</p>
463
+ </div>
464
+
465
+ <!-- ADDED PRINT OPTION BUTTONS -->
466
+ <div class="flex gap-3 w-full mb-3 print-hide">
467
+ <button onclick="window.print()" class="flex-[1] py-3.5 rounded-2xl bg-blue-500/10 hover:bg-blue-500/20 text-blue-700 dark:text-blue-300 font-bold text-sm border border-blue-500/20 transition-all">
468
+ <i data-lucide="printer" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Print Report
469
+ </button>
470
+ </div>
471
+
472
+ <button onclick="switchView('view-home')" class="w-full py-4 rounded-2xl bg-blue-600 hover:bg-blue-700 text-white font-bold text-base shadow-xl active:scale-95 transition-all print-hide">
473
+ Done
474
  </button>
475
  </div>
476
  </div>
477
 
478
+ <!-- FULL SCREEN IMAGE MODAL -->
479
  <div id="image-modal" class="hidden fixed inset-0 z-[200] flex items-center justify-center p-6">
480
  <div class="absolute inset-0 bg-black/80 backdrop-blur-xl" onclick="closeImageModal()"></div>
481
 
482
  <div class="relative w-full max-w-[420px] modal-glass p-6 flex flex-col items-center">
 
483
  <button onclick="closeImageModal()" class="absolute top-4 right-4 p-2.5 bg-white/10 hover:bg-white/20 rounded-full backdrop-blur text-white transition-all z-50">
484
  <i data-lucide="x" class="w-5 h-5"></i>
485
  </button>
 
488
  <h3 class="text-white font-bold text-lg tracking-wider">Image Inspection</h3>
489
  </div>
490
 
 
491
  <div class="relative w-full aspect-square rounded-[1.5rem] overflow-hidden border-2 border-white/20 shadow-inner bg-black cursor-crosshair"
492
  onmouseenter="showModalMagnifier(true)"
493
  onmouseleave="showModalMagnifier(false)"
494
  onmousemove="moveModalMagnifier(event)"
495
  ontouchmove="moveModalMagnifier(event)">
 
496
  <div id="modal-magnifier-glass" class="img-magnifier-glass"></div>
 
497
  <img id="modal-photo" src="" class="w-full h-full object-cover transition-opacity duration-300" />
498
  </div>
499
 
 
500
  <button onclick="toggleHeatmap()" id="btn-modal-heatmap" class="mt-6 w-full py-4 rounded-2xl bg-purple-500/20 hover:bg-purple-500/30 text-white font-bold text-sm border border-purple-500/30 transition-all hidden shadow-lg shadow-purple-500/10">
501
  <i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Toggle AI Heatmap
502
  </button>
503
  </div>
504
  </div>
505
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
  </main>
507
 
508
  <script>
509
+ // --- Core UI Logic ---
510
  window.addEventListener('DOMContentLoaded', () => {
511
  const isDark = document.documentElement.classList.contains('dark');
512
  const metaTheme = document.getElementById('theme-color-meta');
 
543
  setTimeout(() => { if(!v.classList.contains('active')) v.style.display = 'none'; }, 500);
544
  });
545
  const target = document.getElementById(id);
546
+
547
+ document.getElementById('theme-toggle-btn').style.display = 'block';
548
+ setTimeout(() => { target.style.display = 'flex'; void target.offsetWidth; target.classList.add('active'); }, 500);
549
+ }
550
+
551
+ // --- DIABETES DYNAMIC REPORT GENERATOR ---
552
+ function generateDiabetesReport(payload) {
553
+ let analysis = [];
554
+ let foods = [];
555
+ let meds = [];
556
+ let lifestyle = [];
557
+
558
+ let bmi = parseFloat(payload.bmi);
559
+ if (bmi < 18.5) analysis.push(`• BMI: ${bmi} (Underweight)`);
560
+ else if (bmi <= 24.9) analysis.push(`• BMI: ${bmi} (Normal)`);
561
+ else {
562
+ analysis.push(`• BMI: ${bmi} (Overweight/Obese - HIGH)`);
563
+ foods.push("- Focus on a caloric deficit using lean proteins, high-fiber vegetables, and whole grains.");
564
+ lifestyle.push("- Aim for at least 150 minutes of moderate aerobic activity per week for weight management.");
565
+ }
566
+
567
+ let hba1c = parseFloat(payload.HbA1c_level);
568
+ if (hba1c < 5.7) analysis.push(`• HbA1c Level: ${hba1c}% (Normal)`);
569
+ else if (hba1c <= 6.4) {
570
+ analysis.push(`• HbA1c Level: ${hba1c}% (Pre-diabetes range - ELEVATED)`);
571
+ foods.push("- Reduce refined carbohydrates and sugary drinks. Opt for low-glycemic index (GI) foods.");
572
+ } else {
573
+ analysis.push(`• HbA1c Level: ${hba1c}% (Diabetic range - HIGH)`);
574
+ foods.push("- Strictly monitor carbohydrate intake. Incorporate foods like bitter gourd, fenugreek, and high-fiber greens.");
575
+ meds.push("- Consult an endocrinologist for potential initiation of oral hypoglycemics (e.g., Metformin) or insulin therapy.");
576
+ }
577
+
578
+ let glucose = parseFloat(payload.blood_glucose_level);
579
+ if (glucose < 140) analysis.push(`• Blood Glucose: ${glucose} mg/dL (Acceptable)`);
580
+ else {
581
+ analysis.push(`• Blood Glucose: ${glucose} mg/dL (ELEVATED)`);
582
+ lifestyle.push("- Monitor post-meal glucose levels regularly. Stay well hydrated to help kidneys flush excess sugar.");
583
+ if (meds.length === 0) meds.push("- Discuss glucose-lowering medications with a healthcare provider.");
584
+ }
585
+
586
+ if (payload.hypertension === "1") {
587
+ analysis.push(`• Hypertension: Present (High Risk Factor)`);
588
+ foods.push("- Adopt the DASH diet (Dietary Approaches to Stop Hypertension): strictly limit sodium and saturated fats.");
589
+ meds.push("- Review anti-hypertensive medications (e.g., ACE inhibitors/ARBs) with your cardiologist.");
590
+ }
591
+ if (payload.heart_disease === "1") {
592
+ analysis.push(`• Heart Disease: Present (High Risk Factor)`);
593
+ lifestyle.push("- Strict cardiovascular monitoring is critical. Consult a cardiologist before engaging in high-intensity exercise.");
594
  }
595
+
596
+ if(foods.length === 0) foods.push("- Maintain a balanced diet rich in micronutrients to sustain your current healthy metrics.");
597
+ if(meds.length === 0) meds.push("- No specific anti-diabetic medications are indicated based on these metrics.");
598
+ if(lifestyle.length === 0) lifestyle.push("- Continue with regular annual check-ups and maintain an active lifestyle.");
599
+
600
+ document.getElementById('calc-print-analysis').innerText = analysis.join('\n');
601
+ document.getElementById('calc-print-foods').innerText = foods.join('\n');
602
+ document.getElementById('calc-print-meds').innerText = [...new Set(meds)].join('\n');
603
+ document.getElementById('calc-print-lifestyle').innerText = [...new Set(lifestyle)].join('\n');
604
+
605
+ document.getElementById('calc-print-date').textContent = new Date().toLocaleDateString();
606
+ document.getElementById('calc-print-id').textContent = 'DBT-' + Math.floor(Math.random() * 900000 + 100000);
607
+ }
608
+
609
+ function submitDiabetesForm(e) {
610
+ e.preventDefault();
611
+ const btn = document.getElementById('btn-submit-calc');
612
+ const originalHTML = btn.innerHTML;
613
+ btn.innerHTML = `<i data-lucide="loader-2" class="w-5 h-5 animate-spin mx-auto"></i>`;
614
+ btn.classList.add('opacity-70', 'cursor-not-allowed');
615
+ lucide.createIcons();
616
+
617
+ const payload = {
618
+ gender: document.getElementById('calc-gender').value,
619
+ age: document.getElementById('calc-age').value,
620
+ hypertension: document.getElementById('calc-hyper').value,
621
+ heart_disease: document.getElementById('calc-heart').value,
622
+ smoking_history: document.getElementById('calc-smoke').value,
623
+ bmi: document.getElementById('calc-bmi').value,
624
+ HbA1c_level: document.getElementById('calc-hba1c').value,
625
+ blood_glucose_level: document.getElementById('calc-glucose').value
626
+ };
627
+
628
+ fetch('/predict_diabetes', {
629
+ method: 'POST',
630
+ headers: { 'Content-Type': 'application/json' },
631
+ body: JSON.stringify(payload)
632
+ })
633
+ .then(res => res.json())
634
+ .then(data => {
635
+ if(data.error) {
636
+ alert("Error: " + data.error);
637
+ } else {
638
+ const colors = { 'emerald': 'bg-emerald-500', 'orange': 'bg-orange-500', 'rose': 'bg-rose-500' };
639
+ document.getElementById('calc-icon-bg').className = `w-20 h-20 rounded-full flex items-center justify-center mb-6 shadow-xl transition-colors duration-500 ${colors[data.color]}`;
640
+ document.getElementById('calc-icon').setAttribute('data-lucide', data.icon);
641
+ document.getElementById('calc-diagnosis').textContent = data.diagnosis;
642
+ document.getElementById('calc-severity').textContent = data.severity;
643
+ document.getElementById('calc-desc').textContent = data.description;
644
+
645
+ generateDiabetesReport(payload);
646
+ lucide.createIcons();
647
+ switchView('view-calc-result');
648
+ }
649
+ })
650
+ .catch(err => alert("Connection failed."))
651
+ .finally(() => {
652
+ btn.innerHTML = originalHTML;
653
+ btn.classList.remove('opacity-70', 'cursor-not-allowed');
654
+ lucide.createIcons();
655
+ });
656
  }
657
 
658
  function resetApp() {
 
659
  document.getElementById('file-input').value = '';
 
660
  document.getElementById('btn-upload').classList.remove('disabled');
661
  document.getElementById('scanning-section').classList.remove('show');
662
  setTimeout(() => {
 
667
  switchView('view-home');
668
  }
669
 
670
+ let originalImageSrc = ''; let heatmapImageSrc = ''; let isHeatmapShowing = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
 
672
  function triggerUpload() {
673
  if(document.getElementById('btn-upload').classList.contains('disabled')) return;
 
679
  function processImage(fileBlob) {
680
  const reader = new FileReader();
681
  reader.onload = (e) => {
 
682
  document.getElementById('btn-upload').classList.add('disabled');
 
683
  originalImageSrc = e.target.result;
684
  document.getElementById('scan-preview').src = originalImageSrc;
685
  document.getElementById('result-photo').src = originalImageSrc;
 
686
  const scanSection = document.getElementById('scanning-section');
687
  scanSection.style.display = 'flex';
688
  setTimeout(() => { scanSection.classList.add('show'); scanSection.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50);
 
689
  simulateScanProgress();
690
 
691
  const formData = new FormData();
 
694
  fetch('/analyze', { method: 'POST', body: formData })
695
  .then(r => r.json())
696
  .then(data => setTimeout(() => showResult(data), 3200))
697
+ .catch(err => { resetApp(); });
698
  };
699
  reader.readAsDataURL(fileBlob);
700
  }
 
710
  steps.forEach((step, i) => { setTimeout(() => { status.innerText = step.s; detail.innerText = step.d; bar.style.width = step.w; }, i * 800); });
711
  }
712
 
 
713
  const medicalDatabase = {
714
  "No DR": {
715
  desc: "STAGE: NORMAL (No Diabetic Retinopathy).\n\nVISUAL EVIDENCE: The AI identified a healthy vascular structure. No microaneurysms, hemorrhages, or hard exudates were detected in the provided retinal image.",
 
774
  switchView('view-result');
775
  }
776
 
 
777
  function toggleHeatmap() {
778
  isHeatmapShowing = !isHeatmapShowing;
 
779
  const mainImg = document.getElementById('result-photo');
780
  const modalImg = document.getElementById('modal-photo');
781
  const mainBtn = document.getElementById('btn-main-heatmap');
782
  const modalBtn = document.getElementById('btn-modal-heatmap');
 
783
  const targetSrc = isHeatmapShowing ? heatmapImageSrc : originalImageSrc;
 
 
784
  mainImg.style.opacity = '0';
785
  modalImg.style.opacity = '0';
786
 
787
  setTimeout(() => {
 
788
  mainImg.src = targetSrc;
789
  modalImg.src = targetSrc;
 
 
790
  mainImg.style.opacity = '1';
791
  modalImg.style.opacity = '1';
 
 
792
  setTimeout(() => {
793
  if(!document.getElementById('image-modal').classList.contains('hidden')) {
794
  setupModalMagnifier();
 
797
  }, 300);
798
 
799
  if(isHeatmapShowing) {
 
800
  mainBtn.classList.add('bg-purple-500', 'text-white');
801
  mainBtn.classList.remove('bg-purple-500/10', 'text-purple-700', 'dark:text-purple-300');
802
  mainBtn.innerHTML = `<i data-lucide="eye-off" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Original`;
 
 
803
  modalBtn.classList.add('bg-purple-500', 'text-white');
804
  modalBtn.classList.remove('bg-purple-500/20', 'text-white');
805
  modalBtn.innerHTML = `<i data-lucide="eye-off" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Hide AI Heatmap`;
806
  } else {
 
807
  mainBtn.classList.remove('bg-purple-500', 'text-white');
808
  mainBtn.classList.add('bg-purple-500/10', 'text-purple-700', 'dark:text-purple-300');
809
  mainBtn.innerHTML = `<i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Heatmap`;
 
 
810
  modalBtn.classList.remove('bg-purple-500', 'text-white');
811
  modalBtn.classList.add('bg-purple-500/20', 'text-white');
812
  modalBtn.innerHTML = `<i data-lucide="eye" class="w-4 h-4 inline-block -mt-0.5 mr-1"></i> Toggle AI Heatmap`;
 
814
  lucide.createIcons();
815
  }
816
 
 
 
817
  function openImageModal() {
818
  if(window.matchMedia('print').matches) return;
819
  const modal = document.getElementById('image-modal');
820
  const modalImg = document.getElementById('modal-photo');
821
  modalImg.src = isHeatmapShowing ? heatmapImageSrc : originalImageSrc;
 
822
  modal.classList.remove('hidden');
823
  setTimeout(setupModalMagnifier, 150);
824
  }
 
831
  function setupModalMagnifier() {
832
  const img = document.getElementById('modal-photo');
833
  const glass = document.getElementById('modal-magnifier-glass');
 
834
  glass.style.backgroundImage = "url('" + img.src + "')";
835
  glass.style.backgroundRepeat = "no-repeat";
836
  glass.style.backgroundSize = (img.clientWidth * zoomLevel) + "px " + (img.clientHeight * zoomLevel) + "px";
 
846
  const glass = document.getElementById('modal-magnifier-glass');
847
  const pos = getCursorPos(e, img);
848
  let x = pos.x; let y = pos.y;
 
849
  let w = glass.offsetWidth / 2; let h = glass.offsetHeight / 2;
850
  let bw = 3;
 
851
  if (x > img.clientWidth) { x = img.clientWidth; }
852
  if (x < 0) { x = 0; }
853
  if (y > img.clientHeight) { y = img.clientHeight; }
854
  if (y < 0) { y = 0; }
 
855
  glass.style.left = (x - w) + "px";
856
  glass.style.top = (y - h) + "px";
857
  glass.style.backgroundPosition = "-" + ((x * zoomLevel) - w + bw) + "px -" + ((y * zoomLevel) - h + bw) + "px";