Pontonkid commited on
Commit
4a1d0c6
Β·
verified Β·
1 Parent(s): 6fdae13

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +201 -68
src/streamlit_app.py CHANGED
@@ -38,7 +38,7 @@ model = genai.GenerativeModel('gemini-1.5-pro')
38
  # 1. DATABASE SYSTEM
39
  # -----------------------------------------------------------------------------
40
  def init_db():
41
- conn = sqlite3.connect('shinui_v3.db')
42
  c = conn.cursor()
43
  c.execute('''CREATE TABLE IF NOT EXISTS users (email TEXT PRIMARY KEY, password TEXT, joined_date TEXT)''')
44
  c.execute('''CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT, type TEXT, summary TEXT, risk_level TEXT, date TEXT)''')
@@ -48,7 +48,7 @@ def init_db():
48
  def hash_pass(password): return hashlib.sha256(str.encode(password)).hexdigest()
49
 
50
  def register_user(email, password):
51
- conn = sqlite3.connect('shinui_v3.db')
52
  try:
53
  conn.execute("INSERT INTO users VALUES (?, ?, ?)", (email, hash_pass(password), datetime.now().strftime("%Y-%m-%d")))
54
  conn.commit()
@@ -57,31 +57,34 @@ def register_user(email, password):
57
  finally: conn.close()
58
 
59
  def login_user(email, password):
60
- conn = sqlite3.connect('shinui_v3.db')
61
  data = conn.execute("SELECT * FROM users WHERE email=? AND password=?", (email, hash_pass(password))).fetchall()
62
  conn.close()
63
  return data
64
 
65
  def add_history(email, type, summary, risk):
66
- conn = sqlite3.connect('shinui_v3.db')
67
  conn.execute("INSERT INTO history (email, type, summary, risk_level, date) VALUES (?, ?, ?, ?, ?)",
68
  (email, type, summary[:100], risk, datetime.now().strftime("%Y-%m-%d %H:%M")))
69
  conn.commit()
70
  conn.close()
71
 
72
  def get_user_stats(email):
73
- conn = sqlite3.connect('shinui_v3.db')
74
- total = conn.execute("SELECT COUNT(*) FROM history WHERE email=?", (email,)).fetchone()[0]
75
- last = conn.execute("SELECT date FROM history WHERE email=? ORDER BY id DESC LIMIT 1", (email,)).fetchone()
 
 
76
  conn.close()
77
- return total, (last[0] if last else "N/A")
78
 
79
  init_db()
80
 
81
  # -----------------------------------------------------------------------------
82
- # 2. SESSION STATE & UI
83
  # -----------------------------------------------------------------------------
84
  if 'page' not in st.session_state: st.session_state.page = 'landing'
 
85
  if 'logged_in' not in st.session_state: st.session_state.logged_in = False
86
  if 'user_email' not in st.session_state: st.session_state.user_email = ""
87
  if 'analysis_result' not in st.session_state: st.session_state.analysis_result = None
@@ -90,16 +93,51 @@ if 'chat_history' not in st.session_state: st.session_state.chat_history = []
90
 
91
  st.markdown("""
92
  <style>
93
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap');
94
- .stApp { background-color: #09090b; font-family: 'Inter', sans-serif; color: #e4e4e7; }
95
- .meta-card { background-color: #18181b; border: 1px solid #27272a; border-radius: 12px; padding: 24px; margin-bottom: 20px; }
96
- div.stButton > button { background: #3b82f6; color: white; border-radius: 8px; font-weight: 600; width: 100%; }
97
- .risk-bar { height: 10px; border-radius: 5px; margin-top: 5px; background: #333; }
98
- .risk-fill-low { background: #22c55e; width: 33%; height: 100%; border-radius: 5px; }
99
- .risk-fill-med { background: #eab308; width: 66%; height: 100%; border-radius: 5px; }
100
- .risk-fill-high { background: #ef4444; width: 100%; height: 100%; border-radius: 5px; }
101
- .chat-user { background: #27272a; padding: 10px; border-radius: 10px; margin-bottom: 5px; text-align: right; color: #e4e4e7; }
102
- .chat-ai { background: #1e3a8a; padding: 10px; border-radius: 10px; margin-bottom: 5px; text-align: left; color: white; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  #MainMenu, footer, header {visibility: hidden;}
104
  </style>
105
  """, unsafe_allow_html=True)
@@ -111,11 +149,10 @@ def get_gemini_analysis(images, text_context, mode):
111
  role = "a helpful medical assistant"
112
  if mode == "Radiologist Expert": role = "a senior radiologist. Use precise technical terminology."
113
  elif mode == "Simple Explanation": role = "a compassionate doctor explaining to a patient. Use simple analogies."
114
- elif mode == "Medical Student": role = "a medical professor teaching a student. Explain the 'why'."
115
 
116
  prompt = [
117
  f"You are SHINUI, {role}.",
118
- f"Analyze these medical images and this clinical context: '{text_context}'.",
119
  "Strictly format your output into 4 parts separated by '|||'.",
120
  "Part 1: Clinical Findings (What is seen?)",
121
  "Part 2: Risk Assessment (Low/Medium/High)",
@@ -131,21 +168,23 @@ def get_gemini_analysis(images, text_context, mode):
131
  return f"Error|||System Error|||Low|||{str(e)}"
132
 
133
  def chat_with_scan(user_query):
 
134
  prev_findings = st.session_state.analysis_result
135
  images = st.session_state.analysis_images
 
136
  context_prompt = [
137
- "You are SHINUI. We have already analyzed the patient's scan. Here are the previous findings:",
138
- f"--- START FINDINGS ---\n{prev_findings}\n--- END FINDINGS ---",
139
- "The user has a follow-up question based on these findings. Answer directly and clearly.",
 
140
  f"User Question: {user_query}"
141
  ]
142
  full_content = context_prompt + images
143
  try:
144
  response = model.generate_content(full_content)
145
  return response.text
146
- except: return "I could not process that follow-up. Please try rephrasing."
147
 
148
- # Navigation Helper (NO CALLBACKS)
149
  def go_to(page):
150
  st.session_state.page = page
151
  st.rerun()
@@ -155,6 +194,7 @@ def do_sign_out():
155
  st.session_state.analysis_result=None
156
  st.session_state.analysis_images=[]
157
  st.session_state.chat_history=[]
 
158
  go_to('landing')
159
 
160
  # -----------------------------------------------------------------------------
@@ -164,71 +204,159 @@ def do_sign_out():
164
  # --- LANDING ---
165
  def show_landing():
166
  c1, c2 = st.columns([1, 6])
167
- with c1: st.markdown("<h3 style='color:#3b82f6'>SHINUI</h3>", unsafe_allow_html=True)
168
  st.markdown("<br><br>", unsafe_allow_html=True)
169
- col1, col2 = st.columns([1.2, 1])
 
170
  with col1:
171
- st.markdown("<h1 style='font-size: 3.5rem'>Medical Intelligence<br><span style='color:#3b82f6'>Redefined.</span></h1>", unsafe_allow_html=True)
172
- st.markdown("<p style='color:#a1a1aa; font-size:1.1rem'>Multi-image analysis, expert modes, and realtime AI chat.</p>", unsafe_allow_html=True)
173
- b1, b2 = st.columns([1,1])
174
 
175
- # FIXED: Normal buttons, no on_click callback
176
  with b1:
177
- if st.button("Launch App"): go_to('login')
178
  with b2:
179
- if st.button("About"): go_to('about')
180
-
181
  with col2:
182
- st.markdown("<div class='meta-card' style='height:300px; display:flex; justify-content:center; align-items:center;'><h1>🧬</h1></div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- # --- AUTH ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  def show_auth():
186
  c1, c2, c3 = st.columns([1,1,1])
187
  with c2:
188
- st.markdown("<br><br><div class='meta-card'><h2 style='text-align:center'>Welcome</h2>", unsafe_allow_html=True)
189
- tab1, tab2 = st.tabs(["Login", "Register"])
190
- with tab1:
 
 
191
  email = st.text_input("Email", key="l_e")
192
  pw = st.text_input("Password", type="password", key="l_p")
 
193
  if st.button("Sign In"):
194
  if login_user(email, pw):
195
  st.session_state.logged_in=True
196
  st.session_state.user_email=email
197
  go_to('dashboard')
198
- else: st.error("Invalid")
199
- with tab2:
 
 
 
 
 
 
 
200
  re = st.text_input("Email", key="r_e")
201
  rp = st.text_input("Password", type="password", key="r_p")
202
- if st.button("Register"):
203
- if register_user(re, rp): st.success("Created! Login now.")
204
- else: st.error("Taken.")
 
 
 
 
 
 
 
 
 
 
 
205
  st.markdown("</div>", unsafe_allow_html=True)
206
 
207
  # --- DASHBOARD ---
208
  def show_dashboard():
209
- total_scans, last_scan = get_user_stats(st.session_state.user_email)
 
210
  with st.sidebar:
211
  st.markdown(f"### πŸ‘€ {st.session_state.user_email}")
212
- st.markdown("<div style='color:#10b981; margin-bottom:10px'>● Online</div>", unsafe_allow_html=True)
213
- st.markdown("---")
214
- c1, c2 = st.columns(2)
215
- c1.metric("Total Scans", total_scans)
216
- c2.metric("Last Active", last_scan if last_scan != "N/A" else "New")
217
- st.markdown("---")
218
  if st.button("Start New Analysis"):
219
  st.session_state.analysis_result = None
220
  st.session_state.analysis_images = []
221
  st.session_state.chat_history = []
222
  st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  if st.button("Sign Out"): do_sign_out()
224
 
225
- st.title("Advanced Diagnostic Interface")
 
 
226
  if st.session_state.analysis_result is None:
227
- st.markdown("<div class='meta-card'>", unsafe_allow_html=True)
228
  img_files = st.file_uploader("Upload Medical Scans (X-Ray, MRI, CT)", type=['png','jpg','jpeg'], accept_multiple_files=True)
229
  c1, c2 = st.columns([2, 1])
230
  with c1: txt_context = st.text_area("Patient Symptoms / Clinical Context", height=100, placeholder="E.g. Patient has chest pain for 3 weeks...")
231
- with c2: mode = st.selectbox("Analysis Mode", ["Radiologist Expert", "Simple Explanation", "Medical Student", "Research Deep Dive"])
 
 
232
  if st.button("Run SHINUI Analysis"):
233
  if not img_files and not txt_context: st.error("Please upload an image or provide text.")
234
  else:
@@ -242,12 +370,13 @@ def show_dashboard():
242
  add_history(st.session_state.user_email, "Multi-Scan", res, risk_lvl)
243
  st.rerun()
244
  st.markdown("</div>", unsafe_allow_html=True)
 
 
245
  else:
246
  res = st.session_state.analysis_result
247
  images = st.session_state.analysis_images
248
- col_act1, col_act2 = st.columns([1, 4])
249
 
250
- # FIXED: Normal button logic
251
  with col_act1:
252
  if st.button("πŸ’¬ Chat with Scan"): go_to('chat')
253
 
@@ -260,18 +389,24 @@ def show_dashboard():
260
  color = "#22c55e"
261
  if "medium" in sev_clean: bar_class = "risk-fill-med"; color = "#eab308"
262
  if "high" in sev_clean: bar_class = "risk-fill-high"; color = "#ef4444"
263
- st.markdown(f"<div class='meta-card'><h4 style='margin:0; color:{color}'>SEVERITY SCORE: {severity.upper()}</h4><div class='risk-bar'><div class='{bar_class}'></div></div></div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
264
  c1, c2 = st.columns(2)
265
  with c1:
266
- st.markdown(f"<div class='meta-card'><h4 style='color:#3b82f6'>πŸ” Findings</h4>{obs}</div>", unsafe_allow_html=True)
267
- st.markdown(f"<div class='meta-card'><h4 style='color:#f59e0b'>⚠️ Risks</h4>{risks}</div>", unsafe_allow_html=True)
268
  with c2:
269
- st.markdown(f"<div class='meta-card'><h4 style='color:#10b981'>βœ… Protocol</h4>{actions}</div>", unsafe_allow_html=True)
270
  if images: st.image(images[0], caption="Primary Scan Reference", use_container_width=True)
271
 
272
  # --- CHAT PAGE ---
273
  def show_chat():
274
- # FIXED: Normal button logic
275
  if st.button("← Back to Results"): go_to('dashboard')
276
  st.title("πŸ’¬ Chat with Scan")
277
  if st.session_state.analysis_images:
@@ -279,28 +414,26 @@ def show_chat():
279
  cols = st.columns(len(st.session_state.analysis_images))
280
  for idx, img in enumerate(st.session_state.analysis_images):
281
  with cols[idx]: st.image(img, width=150)
 
282
  chat_container = st.container()
283
  user_input = st.chat_input("Ask a follow-up question about the findings...")
 
284
  if user_input:
285
  st.session_state.chat_history.append({"role": "user", "content": user_input})
286
  with st.spinner("Consulting Analysis..."):
287
  ai_reply = chat_with_scan(user_input)
288
  st.session_state.chat_history.append({"role": "ai", "content": ai_reply})
 
289
  with chat_container:
290
  for msg in st.session_state.chat_history:
291
  if msg['role'] == 'user': st.markdown(f"<div class='chat-user'>{msg['content']}</div>", unsafe_allow_html=True)
292
  else: st.markdown(f"<div class='chat-ai'>{msg['content']}</div>", unsafe_allow_html=True)
293
 
294
- # --- ABOUT ---
295
- def show_about():
296
- if st.button("← Back"): go_to('landing')
297
- st.markdown("<br><div class='meta-card'><h2>About SHINUI</h2><p>Advanced Medical AI.</p></div>", unsafe_allow_html=True)
298
-
299
  # -----------------------------------------------------------------------------
300
  # 5. ROUTING
301
  # -----------------------------------------------------------------------------
302
  if st.session_state.page == 'landing': show_landing()
303
- elif st.session_state.page == 'login': show_auth()
304
  elif st.session_state.page == 'dashboard':
305
  if st.session_state.logged_in: show_dashboard()
306
  else: go_to('login')
 
38
  # 1. DATABASE SYSTEM
39
  # -----------------------------------------------------------------------------
40
  def init_db():
41
+ conn = sqlite3.connect('shinui_v5.db')
42
  c = conn.cursor()
43
  c.execute('''CREATE TABLE IF NOT EXISTS users (email TEXT PRIMARY KEY, password TEXT, joined_date TEXT)''')
44
  c.execute('''CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT, type TEXT, summary TEXT, risk_level TEXT, date TEXT)''')
 
48
  def hash_pass(password): return hashlib.sha256(str.encode(password)).hexdigest()
49
 
50
  def register_user(email, password):
51
+ conn = sqlite3.connect('shinui_v5.db')
52
  try:
53
  conn.execute("INSERT INTO users VALUES (?, ?, ?)", (email, hash_pass(password), datetime.now().strftime("%Y-%m-%d")))
54
  conn.commit()
 
57
  finally: conn.close()
58
 
59
  def login_user(email, password):
60
+ conn = sqlite3.connect('shinui_v5.db')
61
  data = conn.execute("SELECT * FROM users WHERE email=? AND password=?", (email, hash_pass(password))).fetchall()
62
  conn.close()
63
  return data
64
 
65
  def add_history(email, type, summary, risk):
66
+ conn = sqlite3.connect('shinui_v5.db')
67
  conn.execute("INSERT INTO history (email, type, summary, risk_level, date) VALUES (?, ?, ?, ?, ?)",
68
  (email, type, summary[:100], risk, datetime.now().strftime("%Y-%m-%d %H:%M")))
69
  conn.commit()
70
  conn.close()
71
 
72
  def get_user_stats(email):
73
+ conn = sqlite3.connect('shinui_v5.db')
74
+ # Get last 5 items for history list
75
+ history = conn.execute("SELECT type, date FROM history WHERE email=? ORDER BY id DESC LIMIT 5", (email,)).fetchall()
76
+ # Get last login date (simplified logic: using last scan date or join date)
77
+ last_scan = history[0][1] if history else "New User"
78
  conn.close()
79
+ return history, last_scan
80
 
81
  init_db()
82
 
83
  # -----------------------------------------------------------------------------
84
+ # 2. SESSION STATE & CSS
85
  # -----------------------------------------------------------------------------
86
  if 'page' not in st.session_state: st.session_state.page = 'landing'
87
+ if 'auth_mode' not in st.session_state: st.session_state.auth_mode = 'login' # Toggles Login/Register view
88
  if 'logged_in' not in st.session_state: st.session_state.logged_in = False
89
  if 'user_email' not in st.session_state: st.session_state.user_email = ""
90
  if 'analysis_result' not in st.session_state: st.session_state.analysis_result = None
 
93
 
94
  st.markdown("""
95
  <style>
96
+ @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;800&display=swap');
97
+
98
+ .stApp {
99
+ background: radial-gradient(circle at 50% 0%, #1e293b 0%, #020617 100%);
100
+ font-family: 'Plus Jakarta Sans', sans-serif;
101
+ color: #f8fafc;
102
+ }
103
+
104
+ .feature-card {
105
+ background: rgba(255, 255, 255, 0.03);
106
+ border: 1px solid rgba(255, 255, 255, 0.05);
107
+ border-radius: 16px;
108
+ padding: 30px;
109
+ text-align: center;
110
+ backdrop-filter: blur(10px);
111
+ margin-bottom: 20px;
112
+ }
113
+
114
+ .result-card {
115
+ background: #0f172a;
116
+ border-radius: 12px;
117
+ padding: 20px;
118
+ margin-bottom: 15px;
119
+ border-left: 4px solid #334155;
120
+ }
121
+
122
+ div.stButton > button {
123
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
124
+ color: white; border: none; border-radius: 8px; font-weight: 600;
125
+ padding: 12px 20px; width: 100%; transition: transform 0.2s;
126
+ }
127
+ div.stButton > button:hover { transform: scale(1.02); }
128
+
129
+ .chat-user { background: #334155; padding: 12px; border-radius: 12px 12px 0 12px; margin-bottom: 10px; text-align: right; margin-left: 20%; }
130
+ .chat-ai { background: #1e293b; border: 1px solid #3b82f6; padding: 12px; border-radius: 12px 12px 12px 0; margin-bottom: 10px; text-align: left; margin-right: 20%; }
131
+
132
+ .history-item {
133
+ font-size: 0.85rem;
134
+ padding: 8px;
135
+ border-bottom: 1px solid rgba(255,255,255,0.05);
136
+ display: flex;
137
+ justify-content: space-between;
138
+ }
139
+ .history-date { color: #64748b; font-size: 0.75rem; }
140
+
141
  #MainMenu, footer, header {visibility: hidden;}
142
  </style>
143
  """, unsafe_allow_html=True)
 
149
  role = "a helpful medical assistant"
150
  if mode == "Radiologist Expert": role = "a senior radiologist. Use precise technical terminology."
151
  elif mode == "Simple Explanation": role = "a compassionate doctor explaining to a patient. Use simple analogies."
 
152
 
153
  prompt = [
154
  f"You are SHINUI, {role}.",
155
+ f"Analyze these medical images and context: '{text_context}'.",
156
  "Strictly format your output into 4 parts separated by '|||'.",
157
  "Part 1: Clinical Findings (What is seen?)",
158
  "Part 2: Risk Assessment (Low/Medium/High)",
 
168
  return f"Error|||System Error|||Low|||{str(e)}"
169
 
170
  def chat_with_scan(user_query):
171
+ # Context-Aware Prompt
172
  prev_findings = st.session_state.analysis_result
173
  images = st.session_state.analysis_images
174
+
175
  context_prompt = [
176
+ "You are SHINUI. You have ALREADY analyzed this patient's scan.",
177
+ f"Here is your previous analysis summary: {prev_findings}",
178
+ "The user is asking a follow-up question. Do NOT re-analyze the whole image from scratch unless asked.",
179
+ "Answer the specific question based on the findings above.",
180
  f"User Question: {user_query}"
181
  ]
182
  full_content = context_prompt + images
183
  try:
184
  response = model.generate_content(full_content)
185
  return response.text
186
+ except: return "I couldn't process that. Please try again."
187
 
 
188
  def go_to(page):
189
  st.session_state.page = page
190
  st.rerun()
 
194
  st.session_state.analysis_result=None
195
  st.session_state.analysis_images=[]
196
  st.session_state.chat_history=[]
197
+ st.session_state.auth_mode = 'login'
198
  go_to('landing')
199
 
200
  # -----------------------------------------------------------------------------
 
204
  # --- LANDING ---
205
  def show_landing():
206
  c1, c2 = st.columns([1, 6])
207
+ with c1: st.markdown("<h3 style='color:#3b82f6; margin:0;'>SHINUI</h3>", unsafe_allow_html=True)
208
  st.markdown("<br><br>", unsafe_allow_html=True)
209
+
210
+ col1, col2 = st.columns([1.3, 1])
211
  with col1:
212
+ st.markdown("<h1 style='font-size: 4rem; line-height: 1.1; margin-bottom:20px;'>Medical Clarity.<br><span style='background: -webkit-linear-gradient(0deg, #3b82f6, #a855f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent;'>Simplified.</span></h1>", unsafe_allow_html=True)
213
+ st.markdown("<p style='color:#94a3b8; font-size:1.2rem; line-height:1.6;'>The advanced AI assistant that translates complex medical scans and notes into clear, actionable insights.</p>", unsafe_allow_html=True)
 
214
 
215
+ b1, b2 = st.columns([1,1.5])
216
  with b1:
217
+ if st.button("Start Now"): go_to('login')
218
  with b2:
219
+ if st.button("How it Works"): go_to('about')
220
+
221
  with col2:
222
+ st.markdown("""
223
+ <div style='text-align:center; animation: float 6s ease-in-out infinite;'>
224
+ <div style='font-size: 7rem;'>🧬</div>
225
+ </div>
226
+ """, unsafe_allow_html=True)
227
+
228
+ # Footer Disclaimer
229
+ st.markdown("<br><br><br><br>", unsafe_allow_html=True)
230
+ st.markdown("""
231
+ <div style='text-align:center; font-size:0.8rem; color:#475569; border-top:1px solid #1e293b; padding-top:20px;'>
232
+ <b>Disclaimer:</b> SHINUI is an AI-powered analysis tool. It does not provide medical diagnosis.
233
+ Always consult a qualified healthcare professional for medical advice.
234
+ </div>
235
+ """, unsafe_allow_html=True)
236
 
237
+ # --- ABOUT ---
238
+ def show_about():
239
+ if st.button("← Back"): go_to('landing')
240
+ st.markdown("<br>", unsafe_allow_html=True)
241
+
242
+ st.markdown("""
243
+ <div style='max-width: 700px; margin: 0 auto; text-align: center;'>
244
+ <h1 style='color:#3b82f6;'>About SHINUI</h1>
245
+ <p style='font-size:1.1rem; line-height:1.8; color:#cbd5e1; margin-bottom:30px;'>
246
+ SHINUI is an AI interface designed to interpret medical data.
247
+ It acts as a bridge between raw diagnostic information (like X-rays and MRI scans)
248
+ and human understanding.
249
+ </p>
250
+
251
+ <div class='feature-card'>
252
+ <h3>How It Works</h3>
253
+ <div style='display:flex; justify-content:space-around; margin-top:20px; text-align:left;'>
254
+ <div>
255
+ <b>1. Upload</b><br>
256
+ <span style='color:#94a3b8; font-size:0.9rem'>Drag & Drop medical scans.</span>
257
+ </div>
258
+ <div>
259
+ <b>2. Analyze</b><br>
260
+ <span style='color:#94a3b8; font-size:0.9rem'>AI detects patterns & risks.</span>
261
+ </div>
262
+ <div>
263
+ <b>3. Understand</b><br>
264
+ <span style='color:#94a3b8; font-size:0.9rem'>Get simple explanations.</span>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ </div>
269
+ """, unsafe_allow_html=True)
270
+
271
+ # --- AUTH (Combined Login/Register with State Switch) ---
272
  def show_auth():
273
  c1, c2, c3 = st.columns([1,1,1])
274
  with c2:
275
+ st.markdown("<br><br>", unsafe_allow_html=True)
276
+ st.markdown("<div class='feature-card' style='text-align:left; padding:40px;'>", unsafe_allow_html=True)
277
+
278
+ if st.session_state.auth_mode == 'login':
279
+ st.markdown("<h2 style='text-align:center; margin-bottom:10px;'>Member Login</h2>", unsafe_allow_html=True)
280
  email = st.text_input("Email", key="l_e")
281
  pw = st.text_input("Password", type="password", key="l_p")
282
+ st.markdown("<br>", unsafe_allow_html=True)
283
  if st.button("Sign In"):
284
  if login_user(email, pw):
285
  st.session_state.logged_in=True
286
  st.session_state.user_email=email
287
  go_to('dashboard')
288
+ else: st.error("Invalid Credentials")
289
+
290
+ st.markdown("<hr style='border-color:rgba(255,255,255,0.1)'>", unsafe_allow_html=True)
291
+ if st.button("Create Account", type="secondary"):
292
+ st.session_state.auth_mode = 'register'
293
+ st.rerun()
294
+
295
+ else: # Register Mode
296
+ st.markdown("<h2 style='text-align:center; margin-bottom:10px;'>Create Account</h2>", unsafe_allow_html=True)
297
  re = st.text_input("Email", key="r_e")
298
  rp = st.text_input("Password", type="password", key="r_p")
299
+ st.markdown("<br>", unsafe_allow_html=True)
300
+ if st.button("Sign Up"):
301
+ if register_user(re, rp):
302
+ st.success("Account created!")
303
+ time.sleep(1)
304
+ st.session_state.auth_mode = 'login' # AUTO-SWITCH TO LOGIN
305
+ st.rerun()
306
+ else: st.error("Email already used.")
307
+
308
+ st.markdown("<hr style='border-color:rgba(255,255,255,0.1)'>", unsafe_allow_html=True)
309
+ if st.button("Back to Login", type="secondary"):
310
+ st.session_state.auth_mode = 'login'
311
+ st.rerun()
312
+
313
  st.markdown("</div>", unsafe_allow_html=True)
314
 
315
  # --- DASHBOARD ---
316
  def show_dashboard():
317
+ history_data, last_active = get_user_stats(st.session_state.user_email)
318
+
319
  with st.sidebar:
320
  st.markdown(f"### πŸ‘€ {st.session_state.user_email}")
321
+ st.markdown("<div style='color:#10b981; margin-bottom:20px'>● Online</div>", unsafe_allow_html=True)
322
+
 
 
 
 
323
  if st.button("Start New Analysis"):
324
  st.session_state.analysis_result = None
325
  st.session_state.analysis_images = []
326
  st.session_state.chat_history = []
327
  st.rerun()
328
+
329
+ st.markdown("---")
330
+ # FIX: Small "Last active" text on top of history
331
+ st.markdown(f"<div style='font-size:0.75rem; color:#64748b; margin-bottom:10px;'>Last active: {last_active}</div>", unsafe_allow_html=True)
332
+ st.markdown("##### Recent Scans")
333
+
334
+ if history_data:
335
+ for h_type, h_date in history_data:
336
+ # FIX: Clean list - Title and Date only
337
+ st.markdown(f"""
338
+ <div class='history-item'>
339
+ <span>{h_type}</span>
340
+ <span class='history-date'>{h_date.split(' ')[0]}</span>
341
+ </div>
342
+ """, unsafe_allow_html=True)
343
+ else:
344
+ st.caption("No scans yet.")
345
+
346
+ st.markdown("---")
347
  if st.button("Sign Out"): do_sign_out()
348
 
349
+ st.title("Diagnostic Interface")
350
+
351
+ # INPUT SECTION
352
  if st.session_state.analysis_result is None:
353
+ st.markdown("<div class='feature-card' style='text-align:left;'>", unsafe_allow_html=True)
354
  img_files = st.file_uploader("Upload Medical Scans (X-Ray, MRI, CT)", type=['png','jpg','jpeg'], accept_multiple_files=True)
355
  c1, c2 = st.columns([2, 1])
356
  with c1: txt_context = st.text_area("Patient Symptoms / Clinical Context", height=100, placeholder="E.g. Patient has chest pain for 3 weeks...")
357
+ with c2: mode = st.selectbox("Analysis Mode", ["Radiologist Expert", "Simple Explanation"])
358
+
359
+ st.markdown("<br>", unsafe_allow_html=True)
360
  if st.button("Run SHINUI Analysis"):
361
  if not img_files and not txt_context: st.error("Please upload an image or provide text.")
362
  else:
 
370
  add_history(st.session_state.user_email, "Multi-Scan", res, risk_lvl)
371
  st.rerun()
372
  st.markdown("</div>", unsafe_allow_html=True)
373
+
374
+ # RESULT SECTION
375
  else:
376
  res = st.session_state.analysis_result
377
  images = st.session_state.analysis_images
 
378
 
379
+ col_act1, col_act2 = st.columns([1, 4])
380
  with col_act1:
381
  if st.button("πŸ’¬ Chat with Scan"): go_to('chat')
382
 
 
389
  color = "#22c55e"
390
  if "medium" in sev_clean: bar_class = "risk-fill-med"; color = "#eab308"
391
  if "high" in sev_clean: bar_class = "risk-fill-high"; color = "#ef4444"
392
+
393
+ st.markdown(f"""
394
+ <div class='feature-card' style='text-align:left; border-color:{color};'>
395
+ <h4 style='margin:0; color:{color}'>SEVERITY SCORE: {severity.upper()}</h4>
396
+ <div class='risk-bar' style='background:rgba(255,255,255,0.1)'><div class='{bar_class}'></div></div>
397
+ </div>
398
+ """, unsafe_allow_html=True)
399
+
400
  c1, c2 = st.columns(2)
401
  with c1:
402
+ st.markdown(f"<div class='result-card' style='border-color:#3b82f6'><h4 style='color:#3b82f6'>πŸ” Findings</h4>{obs}</div>", unsafe_allow_html=True)
403
+ st.markdown(f"<div class='result-card' style='border-color:#f59e0b'><h4 style='color:#f59e0b'>⚠️ Risks</h4>{risks}</div>", unsafe_allow_html=True)
404
  with c2:
405
+ st.markdown(f"<div class='result-card' style='border-color:#10b981'><h4 style='color:#10b981'>βœ… Protocol</h4>{actions}</div>", unsafe_allow_html=True)
406
  if images: st.image(images[0], caption="Primary Scan Reference", use_container_width=True)
407
 
408
  # --- CHAT PAGE ---
409
  def show_chat():
 
410
  if st.button("← Back to Results"): go_to('dashboard')
411
  st.title("πŸ’¬ Chat with Scan")
412
  if st.session_state.analysis_images:
 
414
  cols = st.columns(len(st.session_state.analysis_images))
415
  for idx, img in enumerate(st.session_state.analysis_images):
416
  with cols[idx]: st.image(img, width=150)
417
+
418
  chat_container = st.container()
419
  user_input = st.chat_input("Ask a follow-up question about the findings...")
420
+
421
  if user_input:
422
  st.session_state.chat_history.append({"role": "user", "content": user_input})
423
  with st.spinner("Consulting Analysis..."):
424
  ai_reply = chat_with_scan(user_input)
425
  st.session_state.chat_history.append({"role": "ai", "content": ai_reply})
426
+
427
  with chat_container:
428
  for msg in st.session_state.chat_history:
429
  if msg['role'] == 'user': st.markdown(f"<div class='chat-user'>{msg['content']}</div>", unsafe_allow_html=True)
430
  else: st.markdown(f"<div class='chat-ai'>{msg['content']}</div>", unsafe_allow_html=True)
431
 
 
 
 
 
 
432
  # -----------------------------------------------------------------------------
433
  # 5. ROUTING
434
  # -----------------------------------------------------------------------------
435
  if st.session_state.page == 'landing': show_landing()
436
+ elif st.session_state.page == 'login': show_auth() # Single Auth Page with logic
437
  elif st.session_state.page == 'dashboard':
438
  if st.session_state.logged_in: show_dashboard()
439
  else: go_to('login')