| import sys |
| import os |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) |
|
|
| import streamlit as st |
| from components.chatUI import render_chat |
| from components.history_download import render_history_download |
| from components.hf_loader import render_dataset_panel |
| from components.upload import render_upload_panel |
| from utils.api import health_check |
|
|
| st.set_page_config( |
| page_title="CureMind — AI Medical Assistant", |
| page_icon="🩺", |
| layout="wide", |
| initial_sidebar_state="expanded", |
| ) |
|
|
| |
| st.markdown( |
| """ |
| <style> |
| /* ── Global ── */ |
| #MainMenu, footer { visibility: hidden; } |
| |
| [data-testid="stAppViewContainer"] { |
| background: #F8FAFC; |
| } |
| |
| /* ── Centered responsive main column ── */ |
| .main .block-container { |
| padding-top: 1.5rem; |
| padding-bottom: 2rem; |
| max-width: 820px; |
| margin-left: auto; |
| margin-right: auto; |
| padding-left: 1.5rem; |
| padding-right: 1.5rem; |
| } |
| |
| @media (max-width: 900px) { |
| .main .block-container { |
| padding-left: 1rem; |
| padding-right: 1rem; |
| } |
| } |
| |
| @media (max-width: 600px) { |
| .main .block-container { |
| padding-left: 0.5rem; |
| padding-right: 0.5rem; |
| } |
| [data-testid="stSidebar"] { |
| min-width: 260px !important; |
| } |
| } |
| |
| /* ── Sidebar ── */ |
| [data-testid="stSidebar"] > div:first-child { |
| background: linear-gradient(175deg, #0F172A 0%, #1E293B 100%); |
| padding: 1.5rem 1rem; |
| } |
| |
| /* Make ALL sidebar text light — covers every Streamlit version */ |
| [data-testid="stSidebar"] p, |
| [data-testid="stSidebar"] span, |
| [data-testid="stSidebar"] label, |
| [data-testid="stSidebar"] label *, |
| [data-testid="stSidebar"] .stMarkdown p, |
| [data-testid="stSidebar"] .stCaption, |
| [data-testid="stSidebar"] .stCheckbox label, |
| [data-testid="stSidebar"] .stCheckbox label *, |
| [data-testid="stSidebar"] .stCheckbox p, |
| [data-testid="stSidebar"] .stCheckbox span, |
| [data-testid="stSidebar"] [data-testid="stCheckbox"] label, |
| [data-testid="stSidebar"] [data-testid="stCheckbox"] p, |
| [data-testid="stSidebar"] [data-testid="stCheckbox"] span { |
| color: #CBD5E1 !important; |
| } |
| |
| [data-testid="stSidebar"] .stMarkdown h1, |
| [data-testid="stSidebar"] .stMarkdown h2, |
| [data-testid="stSidebar"] .stMarkdown h3 { |
| color: #F1F5F9 !important; |
| } |
| |
| [data-testid="stSidebar"] hr { |
| border-color: #334155; |
| } |
| |
| [data-testid="stSidebar"] .stButton > button { |
| background: #2563EB; |
| color: #FFFFFF; |
| border: none; |
| border-radius: 8px; |
| font-weight: 600; |
| font-size: 0.85rem; |
| padding: 0.45rem 1rem; |
| width: 100%; |
| transition: background 0.2s ease; |
| } |
| |
| [data-testid="stSidebar"] .stButton > button:hover { |
| background: #1D4ED8; |
| } |
| |
| [data-testid="stSidebar"] .stButton > button:disabled { |
| background: #334155; |
| color: #64748B; |
| } |
| |
| [data-testid="stSidebar"] [data-testid="stFileUploader"] { |
| border: 1px dashed #475569; |
| border-radius: 8px; |
| padding: 0.25rem; |
| background: #1E293B; |
| } |
| |
| /* Browse / Upload button inside the file uploader */ |
| [data-testid="stSidebar"] [data-testid="stFileUploader"] button, |
| [data-testid="stSidebar"] [data-testid="stFileUploaderDropzone"] button { |
| background: #2563EB !important; |
| color: #FFFFFF !important; |
| border: none !important; |
| border-radius: 6px !important; |
| font-weight: 600 !important; |
| } |
| |
| [data-testid="stSidebar"] [data-testid="stFileUploader"] small, |
| [data-testid="stSidebar"] [data-testid="stFileUploaderDropzone"] small { |
| color: #94A3B8 !important; |
| } |
| |
| [data-testid="stSidebar"] [data-testid="stFileUploaderDropzone"] { |
| background: #1E293B !important; |
| border: 1px dashed #475569 !important; |
| border-radius: 8px !important; |
| } |
| |
| [data-testid="stSidebar"] .stCheckbox label { |
| color: #CBD5E1 !important; |
| font-size: 0.85rem; |
| } |
| |
| [data-testid="stSidebar"] .stSlider label { |
| color: #94A3B8 !important; |
| font-size: 0.8rem; |
| } |
| |
| /* Search box in sidebar */ |
| [data-testid="stSidebar"] .stTextInput input { |
| background: #1E293B; |
| border: 1px solid #334155; |
| border-radius: 8px; |
| color: #E2E8F0; |
| font-size: 0.82rem; |
| padding: 0.4rem 0.75rem; |
| } |
| |
| [data-testid="stSidebar"] .stTextInput input::placeholder { |
| color: #64748B; |
| } |
| |
| /* ── Page header ── */ |
| .cm-header { |
| display: flex; |
| align-items: center; |
| gap: 0.6rem; |
| margin-bottom: 0.25rem; |
| } |
| |
| .cm-header-title { |
| font-size: 1.7rem; |
| font-weight: 700; |
| color: #0F172A; |
| margin: 0; |
| letter-spacing: -0.02em; |
| } |
| |
| .cm-badge { |
| background: #EFF6FF; |
| color: #2563EB; |
| font-size: 0.65rem; |
| font-weight: 700; |
| letter-spacing: 0.08em; |
| text-transform: uppercase; |
| padding: 3px 9px; |
| border-radius: 999px; |
| border: 1px solid #BFDBFE; |
| vertical-align: middle; |
| } |
| |
| .cm-subtitle { |
| color: #64748B; |
| font-size: 0.9rem; |
| margin-top: 0.1rem; |
| margin-bottom: 1rem; |
| } |
| |
| /* ── Chat messages ── */ |
| [data-testid="stChatMessageContent"] { |
| font-size: 0.95rem; |
| line-height: 1.7; |
| } |
| |
| /* ── Suggestion chips ── */ |
| .stButton > button { |
| background: #FFFFFF; |
| border: 1px solid #E2E8F0; |
| border-radius: 10px; |
| color: #374151; |
| font-size: 0.82rem; |
| padding: 0.6rem 0.8rem; |
| text-align: center; |
| transition: all 0.15s ease; |
| white-space: normal; |
| height: auto; |
| line-height: 1.4; |
| min-height: 3rem; |
| } |
| |
| .stButton > button:hover { |
| background: #EFF6FF; |
| border-color: #93C5FD; |
| color: #1D4ED8; |
| transform: translateY(-1px); |
| box-shadow: 0 2px 8px rgba(37,99,235,0.12); |
| } |
| |
| /* ── Sources expander ── */ |
| [data-testid="stExpander"] { |
| background: #F1F5F9; |
| border: 1px solid #E2E8F0 !important; |
| border-radius: 8px; |
| margin-top: 0.4rem; |
| } |
| |
| /* ── Welcome area ── */ |
| .cm-welcome { |
| text-align: center; |
| padding: 2rem 1rem 1rem; |
| color: #64748B; |
| font-size: 0.95rem; |
| line-height: 1.6; |
| } |
| |
| /* ── Status dot ── */ |
| .cm-status { |
| display: inline-flex; |
| align-items: center; |
| gap: 5px; |
| font-size: 0.75rem; |
| color: #64748B; |
| } |
| |
| .cm-dot-green { width: 7px; height: 7px; border-radius: 50%; background: #22C55E; display: inline-block; } |
| .cm-dot-red { width: 7px; height: 7px; border-radius: 50%; background: #EF4444; display: inline-block; } |
| |
| /* ── Disclaimer banner ── */ |
| .cm-disclaimer { |
| background: #FFFBEB; |
| border: 1px solid #FDE68A; |
| border-radius: 8px; |
| padding: 0.55rem 1rem; |
| font-size: 0.78rem; |
| color: #92400E; |
| line-height: 1.5; |
| margin-bottom: 0.75rem; |
| } |
| |
| .cm-disclaimer strong { |
| color: #78350F; |
| } |
| </style> |
| """, |
| unsafe_allow_html=True, |
| ) |
|
|
| |
| with st.sidebar: |
| st.markdown( |
| '<p style="font-size:1.4rem;font-weight:700;color:#F1F5F9;margin-bottom:2px;">🩺 CureMind</p>' |
| '<p style="font-size:0.78rem;color:#94A3B8;margin-top:0;">AI Medical Information Assistant</p>', |
| unsafe_allow_html=True, |
| ) |
|
|
| try: |
| health_check() |
| st.markdown( |
| '<span class="cm-status"><span class="cm-dot-green"></span> API online</span>', |
| unsafe_allow_html=True, |
| ) |
| except Exception: |
| st.markdown( |
| '<span class="cm-status"><span class="cm-dot-red"></span> API offline</span>', |
| unsafe_allow_html=True, |
| ) |
|
|
| st.divider() |
| render_upload_panel() |
| st.divider() |
| render_dataset_panel() |
|
|
| |
| st.markdown( |
| '<div class="cm-header">' |
| '<span class="cm-header-title">CureMind</span>' |
| '<span class="cm-badge">Beta</span>' |
| "</div>" |
| '<p class="cm-subtitle">' |
| "Powered by PubMedQA · USMLE · Mental Health Counseling · Groq LLMs" |
| "</p>", |
| unsafe_allow_html=True, |
| ) |
|
|
| st.markdown( |
| '<div class="cm-disclaimer">' |
| "<strong>Beta.</strong> Built as a portfolio project to study retrieval-augmented generation " |
| "and the limits of source-citation as a reliability signal. " |
| "<strong>Not for clinical use.</strong>" |
| "</div>", |
| unsafe_allow_html=True, |
| ) |
|
|
| st.divider() |
|
|
| |
| render_chat() |
|
|
| st.divider() |
|
|
| |
| render_history_download() |
|
|