itsLu commited on
Commit
ba12cc1
·
verified ·
1 Parent(s): dd4dd5e

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +105 -113
templates/index.html CHANGED
@@ -6,22 +6,26 @@
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
 
8
  <style>
9
- :root {
10
  --bg:#0b1220;
11
  --card:#0f1b33;
 
12
  --text:#e8eefc;
13
  --muted:#a9b7d0;
14
- --border:rgba(255,255,255,.08);
15
  --primary:#4f7cff;
 
16
  }
17
 
18
- :root[data-theme="light"] {
19
  --bg:#f6f7fb;
20
  --card:#ffffff;
 
21
  --text:#0c1222;
22
  --muted:#44506a;
23
- --border:rgba(0,0,0,.08);
24
  --primary:#355dff;
 
25
  }
26
 
27
  *{box-sizing:border-box}
@@ -39,38 +43,34 @@ header{
39
  padding:20px 28px;
40
  }
41
 
42
- .brand{
43
- display:flex;
44
- align-items:center;
45
- gap:12px;
46
- }
47
-
48
  .brand img{width:40px;height:40px}
49
 
50
  .chip{
51
- border:1px solid var(--border);
52
- background:var(--card);
53
- padding:6px 12px;
54
  border-radius:999px;
 
 
55
  cursor:pointer;
56
  }
57
 
58
  nav{
59
- padding:0 28px 20px;
60
  display:flex;
61
  gap:10px;
62
  }
63
 
64
  .tab{
65
- border:1px solid var(--border);
66
- background:transparent;
67
- padding:6px 14px;
68
  border-radius:999px;
 
 
69
  cursor:pointer;
70
  }
71
 
72
  .tab.active{
73
- background:rgba(79,124,255,.15);
 
74
  }
75
 
76
  .section{display:none}
@@ -82,55 +82,61 @@ main{
82
  gap:24px;
83
  padding:0 28px 28px;
84
  }
85
-
86
- @media(max-width:900px){
87
- main{grid-template-columns:1fr}
88
- }
89
 
90
  .card{
91
- background:var(--card);
92
- border:1px solid var(--border);
93
  border-radius:18px;
 
94
  padding:22px;
95
  }
96
 
97
- h2{margin-top:0;font-size:20px}
98
 
99
  .controls{
100
  display:flex;
101
  gap:12px;
102
  flex-wrap:wrap;
103
- margin-bottom:14px;
 
 
 
 
 
 
 
 
 
 
 
104
  }
105
 
106
  select{
107
  appearance:none;
108
- padding:10px 14px;
109
  border-radius:12px;
110
  border:1px solid var(--border);
111
- background:var(--card);
112
  color:var(--text);
113
- min-width:240px;
114
- font-weight:500;
115
  }
116
 
117
- select:hover{border-color:var(--primary)}
118
-
119
  .file-btn{
120
  padding:10px 14px;
121
  border-radius:12px;
122
  border:1px dashed var(--border);
123
- background:transparent;
124
  cursor:pointer;
 
125
  }
126
 
127
  button.primary{
128
  padding:10px 18px;
129
  border-radius:12px;
130
  border:none;
131
- background:var(--primary);
132
  color:white;
133
- font-weight:600;
134
  cursor:pointer;
135
  }
136
 
@@ -138,12 +144,14 @@ button.secondary{
138
  padding:10px 14px;
139
  border-radius:12px;
140
  border:1px solid var(--border);
141
- background:transparent;
142
  cursor:pointer;
143
  }
144
 
 
 
145
  .upload-box{
146
- margin-top:12px;
147
  padding:18px;
148
  border:2px dashed var(--border);
149
  border-radius:14px;
@@ -151,69 +159,44 @@ button.secondary{
151
  color:var(--muted);
152
  }
153
 
154
- .upload-box.dragover{
155
- background:rgba(79,124,255,.08);
156
- }
157
-
158
- .preview{
159
- margin-top:14px;
160
- background:black;
161
- border-radius:14px;
162
- overflow:hidden;
163
- max-height:360px;
164
- }
165
  .preview.hidden{display:none}
 
166
 
167
- .preview img{
168
- width:100%;
169
- height:100%;
170
- object-fit:contain;
171
- }
172
-
173
- .result{
174
- margin-top:16px;
175
- }
176
 
177
- .result-title{
178
- font-size:22px;
179
- font-weight:700;
180
- }
181
-
182
- .result-msg{color:var(--muted)}
183
-
184
- .examples img{
185
- width:100%;
186
- max-height:280px;
187
- object-fit:contain;
188
- background:black;
189
  border-radius:14px;
190
- margin-top:10px;
191
  }
 
 
 
192
 
193
  .team{
194
  display:grid;
195
- grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
196
  gap:14px;
197
  }
198
-
199
  .member{
200
  display:flex;
201
- align-items:center;
202
  gap:12px;
 
 
 
 
203
  }
204
-
205
- .member img{
206
- width:48px;
207
- height:48px;
208
- border-radius:50%;
209
- cursor:pointer;
210
- }
211
 
212
  footer{
213
  padding:14px 28px;
 
214
  font-size:13px;
215
  color:var(--muted);
216
- border-top:1px solid var(--border);
217
  }
218
 
219
  /* modal */
@@ -226,13 +209,12 @@ footer{
226
  justify-content:center;
227
  }
228
  .modal-backdrop.show{display:flex}
229
-
230
  .modal{
231
  background:var(--card);
232
- border-radius:16px;
233
  padding:18px;
234
- width:420px;
235
  border:1px solid var(--border);
 
236
  }
237
  </style>
238
  </head>
@@ -244,7 +226,7 @@ footer{
244
  <img src="{{ url_for('static', filename='assets/brain/brain.svg') }}">
245
  <div>
246
  <strong>NeuraScan</strong><br>
247
- <small class="muted">AI-assisted MRI screening (demo)</small>
248
  </div>
249
  </div>
250
  <button id="themeToggle" class="chip">☀️</button>
@@ -261,7 +243,7 @@ footer{
261
  <h2>New scan</h2>
262
 
263
  <div class="controls">
264
- <select id="modelSelect"></select>
265
 
266
  <label class="file-btn">
267
  Choose image
@@ -283,28 +265,22 @@ Drag & drop or paste (Ctrl+V) an MRI image
283
  <div class="result">
284
  <div id="resultTitle" class="result-title">—</div>
285
  <div id="resultMsg" class="result-msg">Upload an image to begin.</div>
286
- <button id="likelyBtn" style="display:none;margin-top:10px" onclick="showLikely()">Show most likely result</button>
287
  </div>
288
  </div>
289
 
290
  <div class="card">
291
  <h2>Examples</h2>
292
-
293
  <div class="examples">
294
- <div style="margin-top:10px;">
295
- <div style="font-weight:700;margin-bottom:6px;">Supported example</div>
296
- <div class="result-msg" style="margin-bottom:10px;">
297
- Brain-only MRI slice (no skull visible).
298
- </div>
299
- <img src="{{ url_for('static', filename='assets/brain/unsupportedexample.jpg') }}" alt="Supported example: brain-only MRI">
300
  </div>
301
-
302
- <div style="margin-top:16px;">
303
- <div style="font-weight:700;margin-bottom:6px;">Unsupported example</div>
304
- <div class="result-msg" style="margin-bottom:10px;">
305
- Skull visible — please upload a brain-only slice.
306
- </div>
307
- <img src="{{ url_for('static', filename='assets/brain/supportedexample.jpg') }}" alt="Unsupported example: skull visible">
308
  </div>
309
  </div>
310
  </div>
@@ -315,10 +291,7 @@ Drag & drop or paste (Ctrl+V) an MRI image
315
  <main>
316
  <div class="card">
317
  <h2>About NeuraScan</h2>
318
- <p>
319
- Educational demo for Alzheimer’s stage classification.
320
- When confidence is low, the system reports <b>Uncertain</b>.
321
- </p>
322
 
323
  <h2>Under the supervision of:</h2>
324
  <p>
@@ -344,13 +317,12 @@ Contact: <a href="mailto:mohamedasem318@gmail.com">Mohamed Assem</a> •
344
  Educational demo — not medical advice
345
  </footer>
346
 
347
- <!-- modal -->
348
  <div class="modal-backdrop" id="modalBackdrop" onclick="closeModalIfBackdrop(event)">
349
  <div class="modal">
350
  <h3>Most likely result</h3>
351
  <p id="modalLabel"></p>
352
  <p id="modalProb"></p>
353
- <button onclick="closeModal()">Close</button>
354
  </div>
355
  </div>
356
 
@@ -382,15 +354,14 @@ resultTitle.textContent="—";
382
  resultMsg.textContent="Ready to run scan.";
383
  likelyBtn.style.display="none";
384
  newBtn.style.display="none";
 
385
  }
386
 
387
  fileInput.onchange=e=>e.target.files[0]&&setFile(e.target.files[0]);
388
 
389
- dropZone.ondragover=e=>{e.preventDefault();dropZone.classList.add("dragover")};
390
- dropZone.ondragleave=()=>dropZone.classList.remove("dragover");
391
  dropZone.ondrop=e=>{
392
  e.preventDefault();
393
- dropZone.classList.remove("dragover");
394
  e.dataTransfer.files[0]&&setFile(e.dataTransfer.files[0]);
395
  };
396
 
@@ -415,45 +386,66 @@ modelSelect.value=d.default_model_id;
415
  async function runScan(){
416
  if(!currentFile){resultMsg.textContent="Upload an image first.";return;}
417
  runBtn.disabled=true;
 
418
  resultTitle.textContent="Running…";
 
 
 
 
 
419
  const fd=new FormData();
420
  fd.append("file",currentFile);
421
  fd.append("model_id",modelSelect.value);
422
  const r=await fetch("/api/classify",{method:"POST",body:fd});
423
  const d=await r.json();
 
424
  if(d.prediction.label==="Uncertain"){
425
  resultTitle.textContent="Uncertain";
426
  resultMsg.textContent="Consult a professional.";
 
 
 
427
  likelyBtn.style.display="inline-block";
428
- lastMostLikely=d.probabilities.sort((a,b)=>b.prob-a.prob)[0];
429
  }else{
430
  resultTitle.textContent=d.prediction.label;
431
  resultMsg.textContent=`Confidence: ${(d.prediction.confidence*100).toFixed(1)}%`;
 
 
432
  }
433
  newBtn.style.display="inline-block";
 
 
 
 
434
  runBtn.disabled=false;
 
 
435
  }
436
 
437
  function newScan(){
438
  currentFile=null;
439
  fileInput.value="";
440
- previewImg.src="";
441
  previewBox.classList.add("hidden");
 
442
  resultTitle.textContent="—";
443
  resultMsg.textContent="Upload an image to begin.";
444
  likelyBtn.style.display="none";
445
  newBtn.style.display="none";
 
446
  }
447
 
448
  function showLikely(){
 
449
  modalLabel.textContent=lastMostLikely.label;
450
  modalProb.textContent=`Probability: ${(lastMostLikely.prob*100).toFixed(1)}%`;
451
  modalBackdrop.classList.add("show");
452
  }
453
  function closeModal(){modalBackdrop.classList.remove("show")}
454
- function closeModalIfBackdrop(e){e.target.id==="modalBackdrop"&&closeModal()}
455
 
456
  loadModels();
457
  </script>
 
458
  </body>
459
  </html>
 
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
 
8
  <style>
9
+ :root{
10
  --bg:#0b1220;
11
  --card:#0f1b33;
12
+ --card2:#0c172e;
13
  --text:#e8eefc;
14
  --muted:#a9b7d0;
15
+ --border:rgba(255,255,255,.12);
16
  --primary:#4f7cff;
17
+ --ring:rgba(79,124,255,.35);
18
  }
19
 
20
+ :root[data-theme="light"]{
21
  --bg:#f6f7fb;
22
  --card:#ffffff;
23
+ --card2:#ffffff;
24
  --text:#0c1222;
25
  --muted:#44506a;
26
+ --border:rgba(0,0,0,.12);
27
  --primary:#355dff;
28
+ --ring:rgba(53,93,255,.25);
29
  }
30
 
31
  *{box-sizing:border-box}
 
43
  padding:20px 28px;
44
  }
45
 
46
+ .brand{display:flex;gap:12px;align-items:center}
 
 
 
 
 
47
  .brand img{width:40px;height:40px}
48
 
49
  .chip{
50
+ padding:8px 12px;
 
 
51
  border-radius:999px;
52
+ background:var(--card);
53
+ border:1px solid var(--border);
54
  cursor:pointer;
55
  }
56
 
57
  nav{
58
+ padding:0 28px 18px;
59
  display:flex;
60
  gap:10px;
61
  }
62
 
63
  .tab{
64
+ padding:8px 16px;
 
 
65
  border-radius:999px;
66
+ border:1px solid var(--border);
67
+ background:rgba(255,255,255,.06);
68
  cursor:pointer;
69
  }
70
 
71
  .tab.active{
72
+ background:rgba(79,124,255,.18);
73
+ box-shadow:0 0 0 4px var(--ring);
74
  }
75
 
76
  .section{display:none}
 
82
  gap:24px;
83
  padding:0 28px 28px;
84
  }
85
+ @media(max-width:900px){main{grid-template-columns:1fr}}
 
 
 
86
 
87
  .card{
88
+ background:linear-gradient(180deg,var(--card),var(--card2));
 
89
  border-radius:18px;
90
+ border:1px solid var(--border);
91
  padding:22px;
92
  }
93
 
94
+ h2{margin:0 0 12px;font-size:20px}
95
 
96
  .controls{
97
  display:flex;
98
  gap:12px;
99
  flex-wrap:wrap;
100
+ align-items:center;
101
+ }
102
+
103
+ .select-wrap{position:relative}
104
+ .select-wrap::after{
105
+ content:"▾";
106
+ position:absolute;
107
+ right:14px;
108
+ top:50%;
109
+ transform:translateY(-50%);
110
+ pointer-events:none;
111
+ color:var(--muted);
112
  }
113
 
114
  select{
115
  appearance:none;
116
+ padding:10px 40px 10px 14px;
117
  border-radius:12px;
118
  border:1px solid var(--border);
119
+ background:rgba(255,255,255,.06);
120
  color:var(--text);
121
+ font-weight:600;
122
+ min-width:260px;
123
  }
124
 
 
 
125
  .file-btn{
126
  padding:10px 14px;
127
  border-radius:12px;
128
  border:1px dashed var(--border);
 
129
  cursor:pointer;
130
+ background:rgba(255,255,255,.04);
131
  }
132
 
133
  button.primary{
134
  padding:10px 18px;
135
  border-radius:12px;
136
  border:none;
137
+ background:linear-gradient(180deg,var(--primary),#2f62ff);
138
  color:white;
139
+ font-weight:700;
140
  cursor:pointer;
141
  }
142
 
 
144
  padding:10px 14px;
145
  border-radius:12px;
146
  border:1px solid var(--border);
147
+ background:rgba(255,255,255,.04);
148
  cursor:pointer;
149
  }
150
 
151
+ button:disabled{opacity:.6}
152
+
153
  .upload-box{
154
+ margin-top:14px;
155
  padding:18px;
156
  border:2px dashed var(--border);
157
  border-radius:14px;
 
159
  color:var(--muted);
160
  }
161
 
162
+ .preview{margin-top:14px;background:black;border-radius:14px;overflow:hidden}
 
 
 
 
 
 
 
 
 
 
163
  .preview.hidden{display:none}
164
+ .preview img{width:100%;max-height:360px;object-fit:contain}
165
 
166
+ .result{margin-top:16px}
167
+ .result-title{font-size:22px;font-weight:800}
168
+ .result-msg{color:var(--muted);margin-top:6px}
 
 
 
 
 
 
169
 
170
+ .examples .ex{
171
+ margin-top:12px;
172
+ border:1px solid var(--border);
 
 
 
 
 
 
 
 
 
173
  border-radius:14px;
174
+ overflow:hidden;
175
  }
176
+ .examples .lbl{padding:10px;font-weight:800;border-bottom:1px solid var(--border)}
177
+ .examples .desc{padding:0 10px 10px;color:var(--muted)}
178
+ .examples img{width:100%;max-height:280px;object-fit:contain;background:black}
179
 
180
  .team{
181
  display:grid;
182
+ grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
183
  gap:14px;
184
  }
 
185
  .member{
186
  display:flex;
 
187
  gap:12px;
188
+ align-items:center;
189
+ padding:10px;
190
+ border:1px solid var(--border);
191
+ border-radius:14px;
192
  }
193
+ .member img{width:48px;height:48px;border-radius:50%;cursor:pointer}
 
 
 
 
 
 
194
 
195
  footer{
196
  padding:14px 28px;
197
+ border-top:1px solid var(--border);
198
  font-size:13px;
199
  color:var(--muted);
 
200
  }
201
 
202
  /* modal */
 
209
  justify-content:center;
210
  }
211
  .modal-backdrop.show{display:flex}
 
212
  .modal{
213
  background:var(--card);
 
214
  padding:18px;
215
+ border-radius:16px;
216
  border:1px solid var(--border);
217
+ width:420px;
218
  }
219
  </style>
220
  </head>
 
226
  <img src="{{ url_for('static', filename='assets/brain/brain.svg') }}">
227
  <div>
228
  <strong>NeuraScan</strong><br>
229
+ <small style="color:var(--muted)">AI-assisted MRI screening (demo)</small>
230
  </div>
231
  </div>
232
  <button id="themeToggle" class="chip">☀️</button>
 
243
  <h2>New scan</h2>
244
 
245
  <div class="controls">
246
+ <span class="select-wrap"><select id="modelSelect"></select></span>
247
 
248
  <label class="file-btn">
249
  Choose image
 
265
  <div class="result">
266
  <div id="resultTitle" class="result-title">—</div>
267
  <div id="resultMsg" class="result-msg">Upload an image to begin.</div>
268
+ <button id="likelyBtn" class="secondary" style="display:none;margin-top:10px" onclick="showLikely()">Show most likely result</button>
269
  </div>
270
  </div>
271
 
272
  <div class="card">
273
  <h2>Examples</h2>
 
274
  <div class="examples">
275
+ <div class="ex">
276
+ <div class="lbl">Supported example</div>
277
+ <div class="desc">Brain-only MRI slice (no skull visible).</div>
278
+ <img src="{{ url_for('static', filename='assets/brain/supportedexample.jpg') }}">
 
 
279
  </div>
280
+ <div class="ex">
281
+ <div class="lbl">Unsupported example</div>
282
+ <div class="desc">Skull visible — please upload a brain-only slice.</div>
283
+ <img src="{{ url_for('static', filename='assets/brain/unsupportedexample.jpg') }}">
 
 
 
284
  </div>
285
  </div>
286
  </div>
 
291
  <main>
292
  <div class="card">
293
  <h2>About NeuraScan</h2>
294
+ <p>Educational demo for Alzheimer’s stage classification. Low confidence → <b>Uncertain</b>.</p>
 
 
 
295
 
296
  <h2>Under the supervision of:</h2>
297
  <p>
 
317
  Educational demo — not medical advice
318
  </footer>
319
 
 
320
  <div class="modal-backdrop" id="modalBackdrop" onclick="closeModalIfBackdrop(event)">
321
  <div class="modal">
322
  <h3>Most likely result</h3>
323
  <p id="modalLabel"></p>
324
  <p id="modalProb"></p>
325
+ <button class="secondary" onclick="closeModal()">Close</button>
326
  </div>
327
  </div>
328
 
 
354
  resultMsg.textContent="Ready to run scan.";
355
  likelyBtn.style.display="none";
356
  newBtn.style.display="none";
357
+ lastMostLikely=null;
358
  }
359
 
360
  fileInput.onchange=e=>e.target.files[0]&&setFile(e.target.files[0]);
361
 
362
+ dropZone.ondragover=e=>{e.preventDefault()};
 
363
  dropZone.ondrop=e=>{
364
  e.preventDefault();
 
365
  e.dataTransfer.files[0]&&setFile(e.dataTransfer.files[0]);
366
  };
367
 
 
386
  async function runScan(){
387
  if(!currentFile){resultMsg.textContent="Upload an image first.";return;}
388
  runBtn.disabled=true;
389
+ modelSelect.disabled=true;
390
  resultTitle.textContent="Running…";
391
+ resultMsg.textContent="Analyzing image…";
392
+ likelyBtn.style.display="none";
393
+ lastMostLikely=null;
394
+
395
+ try{
396
  const fd=new FormData();
397
  fd.append("file",currentFile);
398
  fd.append("model_id",modelSelect.value);
399
  const r=await fetch("/api/classify",{method:"POST",body:fd});
400
  const d=await r.json();
401
+
402
  if(d.prediction.label==="Uncertain"){
403
  resultTitle.textContent="Uncertain";
404
  resultMsg.textContent="Consult a professional.";
405
+ const probs=[...(d.probabilities||[])].sort((a,b)=>b.prob-a.prob);
406
+ if(probs[0]){
407
+ lastMostLikely=probs[0];
408
  likelyBtn.style.display="inline-block";
409
+ }
410
  }else{
411
  resultTitle.textContent=d.prediction.label;
412
  resultMsg.textContent=`Confidence: ${(d.prediction.confidence*100).toFixed(1)}%`;
413
+ likelyBtn.style.display="none";
414
+ lastMostLikely=null;
415
  }
416
  newBtn.style.display="inline-block";
417
+ }catch(e){
418
+ resultTitle.textContent="Sorry";
419
+ resultMsg.textContent="Something went wrong.";
420
+ }finally{
421
  runBtn.disabled=false;
422
+ modelSelect.disabled=false;
423
+ }
424
  }
425
 
426
  function newScan(){
427
  currentFile=null;
428
  fileInput.value="";
 
429
  previewBox.classList.add("hidden");
430
+ previewImg.src="";
431
  resultTitle.textContent="—";
432
  resultMsg.textContent="Upload an image to begin.";
433
  likelyBtn.style.display="none";
434
  newBtn.style.display="none";
435
+ lastMostLikely=null;
436
  }
437
 
438
  function showLikely(){
439
+ if(!lastMostLikely)return;
440
  modalLabel.textContent=lastMostLikely.label;
441
  modalProb.textContent=`Probability: ${(lastMostLikely.prob*100).toFixed(1)}%`;
442
  modalBackdrop.classList.add("show");
443
  }
444
  function closeModal(){modalBackdrop.classList.remove("show")}
445
+ function closeModalIfBackdrop(e){if(e.target.id==="modalBackdrop")closeModal()}
446
 
447
  loadModels();
448
  </script>
449
+
450
  </body>
451
  </html>