Commit ยท
f5c92f8
1
Parent(s): c4ebb42
Apply styled UI updates to app.py
Browse files
app.py
CHANGED
|
@@ -16,7 +16,104 @@ import os
|
|
| 16 |
# -----------------------------------------------------------------------------
|
| 17 |
# 1. CONFIG & CSS
|
| 18 |
# -----------------------------------------------------------------------------
|
| 19 |
-
st.set_page_config(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
st.markdown("""
|
| 22 |
<style>
|
|
@@ -543,24 +640,48 @@ if not st.session_state.get("logged_in") and not st.session_state.get("local_byp
|
|
| 543 |
|
| 544 |
# Show profile and sign out in sidebar
|
| 545 |
with st.sidebar:
|
|
|
|
|
|
|
| 546 |
name = st.session_state.get("user_name", "Student")
|
| 547 |
email = st.session_state.get("user_email", "")
|
| 548 |
avatar = st.session_state.get("user_avatar", "")
|
| 549 |
-
|
| 550 |
-
|
|
|
|
|
|
|
| 551 |
if avatar:
|
| 552 |
-
st.image(avatar, width=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 553 |
st.markdown(f"**{name}**")
|
| 554 |
-
st.caption(email)
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
st.
|
| 562 |
-
|
| 563 |
-
|
|
|
|
| 564 |
|
| 565 |
# Session State
|
| 566 |
if "timer_running" not in st.session_state: st.session_state.timer_running = False
|
|
@@ -1055,11 +1176,33 @@ else:
|
|
| 1055 |
if not st.session_state.focus_mode:
|
| 1056 |
with left_col:
|
| 1057 |
# ====== USER INFO & SIGN-OUT REMOVED (now in sidebar) ======
|
| 1058 |
-
st.markdown("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1059 |
|
| 1060 |
# Timer Widget
|
| 1061 |
with st.container(border=True):
|
| 1062 |
-
st.markdown(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1063 |
|
| 1064 |
# Timer Logic
|
| 1065 |
total_seconds = (st.session_state.time_left_m * 60) + st.session_state.time_left_s
|
|
@@ -1200,14 +1343,15 @@ if not st.session_state.focus_mode:
|
|
| 1200 |
with st.container(border=True):
|
| 1201 |
# Connectivity Check
|
| 1202 |
is_online = check_internet()
|
| 1203 |
-
status_color = "online" if is_online else "offline"
|
| 1204 |
status_text = "ONLINE" if is_online else "OFFLINE"
|
| 1205 |
|
| 1206 |
st.markdown(f"""
|
| 1207 |
-
|
| 1208 |
-
|
| 1209 |
-
|
| 1210 |
-
|
|
|
|
| 1211 |
""", unsafe_allow_html=True)
|
| 1212 |
|
| 1213 |
# Tabs
|
|
@@ -1232,12 +1376,13 @@ if not st.session_state.focus_mode:
|
|
| 1232 |
|
| 1233 |
c1, c2 = st.columns([0.85, 0.15])
|
| 1234 |
with c1:
|
| 1235 |
-
|
| 1236 |
-
|
| 1237 |
-
<
|
| 1238 |
-
|
| 1239 |
-
|
| 1240 |
-
|
|
|
|
| 1241 |
with c2:
|
| 1242 |
if st.button("๐๏ธ", key=f"del_{src['id']}", help="Delete source", type="tertiary"):
|
| 1243 |
try:
|
|
@@ -1249,10 +1394,11 @@ if not st.session_state.focus_mode:
|
|
| 1249 |
st.error(f"Error: {e}")
|
| 1250 |
else:
|
| 1251 |
st.markdown("""
|
| 1252 |
-
<div style=
|
| 1253 |
-
|
|
|
|
| 1254 |
</div>
|
| 1255 |
-
|
| 1256 |
|
| 1257 |
# --- Add Source Section ---
|
| 1258 |
st.markdown("<br>", unsafe_allow_html=True)
|
|
@@ -1524,7 +1670,16 @@ if not st.session_state.focus_mode:
|
|
| 1524 |
# Header
|
| 1525 |
h_col1, h_col2 = st.columns([0.8, 0.2])
|
| 1526 |
with h_col1:
|
| 1527 |
-
st.markdown("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1528 |
with h_col2:
|
| 1529 |
if st.button("๐ Analytics"):
|
| 1530 |
show_analytics_dialog()
|
|
@@ -1540,13 +1695,38 @@ if not st.session_state.focus_mode:
|
|
| 1540 |
with chat_container:
|
| 1541 |
if not st.session_state.chat_history:
|
| 1542 |
# Welcome Content
|
| 1543 |
-
st.markdown('<div class="article-title">Welcome to FocusFlow</div>', unsafe_allow_html=True)
|
| 1544 |
st.markdown("""
|
| 1545 |
-
<div
|
| 1546 |
-
|
| 1547 |
-
|
| 1548 |
-
|
| 1549 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1550 |
</div>
|
| 1551 |
""", unsafe_allow_html=True)
|
| 1552 |
else:
|
|
@@ -1619,8 +1799,18 @@ if not st.session_state.focus_mode:
|
|
| 1619 |
if right_col:
|
| 1620 |
with right_col:
|
| 1621 |
# --- CALENDAR WIDGET ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1622 |
today = date.today()
|
| 1623 |
-
selected_date = st.date_input("
|
| 1624 |
|
| 1625 |
# --- LOGIC: Show plan for selected date ---
|
| 1626 |
# If user selects a future date, show its plan inline
|
|
|
|
| 16 |
# -----------------------------------------------------------------------------
|
| 17 |
# 1. CONFIG & CSS
|
| 18 |
# -----------------------------------------------------------------------------
|
| 19 |
+
st.set_page_config(
|
| 20 |
+
page_title="FocusFlow",
|
| 21 |
+
page_icon="๐ฏ",
|
| 22 |
+
layout="wide",
|
| 23 |
+
initial_sidebar_state="expanded"
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
st.markdown("""
|
| 27 |
+
<style>
|
| 28 |
+
/* Main background */
|
| 29 |
+
.stApp {
|
| 30 |
+
background-color: #0F0F1A;
|
| 31 |
+
color: #E2E8F0;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/* Sidebar styling */
|
| 35 |
+
[data-testid="stSidebar"] {
|
| 36 |
+
background-color: #1A1A2E;
|
| 37 |
+
border-right: 1px solid #2D2D44;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
/* Cards */
|
| 41 |
+
[data-testid="stExpander"] {
|
| 42 |
+
background-color: #1E1E30;
|
| 43 |
+
border: 1px solid #2D2D44;
|
| 44 |
+
border-radius: 10px;
|
| 45 |
+
margin-bottom: 8px;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
/* Buttons */
|
| 49 |
+
.stButton > button {
|
| 50 |
+
background: linear-gradient(135deg, #7C3AED, #3B82F6);
|
| 51 |
+
color: white;
|
| 52 |
+
border: none;
|
| 53 |
+
border-radius: 8px;
|
| 54 |
+
font-weight: 600;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.stButton > button:hover {
|
| 58 |
+
background: linear-gradient(135deg, #6D28D9, #2563EB);
|
| 59 |
+
transform: translateY(-1px);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
/* Sign out button override */
|
| 63 |
+
.stButton > button[kind="secondary"] {
|
| 64 |
+
background: #374151;
|
| 65 |
+
color: #E2E8F0;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/* Input fields */
|
| 69 |
+
.stTextInput > div > div > input,
|
| 70 |
+
.stTextArea > div > div > textarea {
|
| 71 |
+
background-color: #1E1E30;
|
| 72 |
+
color: #E2E8F0;
|
| 73 |
+
border: 1px solid #374151;
|
| 74 |
+
border-radius: 8px;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/* Section headers */
|
| 78 |
+
h1, h2, h3 {
|
| 79 |
+
color: #E2E8F0;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/* Metric cards */
|
| 83 |
+
[data-testid="metric-container"] {
|
| 84 |
+
background-color: #1E1E30;
|
| 85 |
+
border: 1px solid #2D2D44;
|
| 86 |
+
border-radius: 10px;
|
| 87 |
+
padding: 16px;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
/* Hide streamlit branding */
|
| 91 |
+
#MainMenu {visibility: hidden;}
|
| 92 |
+
footer {visibility: hidden;}
|
| 93 |
+
|
| 94 |
+
/* Online badge */
|
| 95 |
+
.online-badge {
|
| 96 |
+
background: #10B981;
|
| 97 |
+
color: white;
|
| 98 |
+
padding: 2px 10px;
|
| 99 |
+
border-radius: 20px;
|
| 100 |
+
font-size: 12px;
|
| 101 |
+
font-weight: bold;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
/* Source cards */
|
| 105 |
+
.source-card {
|
| 106 |
+
background: #1E1E30;
|
| 107 |
+
border: 1px solid #374151;
|
| 108 |
+
border-radius: 8px;
|
| 109 |
+
padding: 10px 14px;
|
| 110 |
+
margin: 6px 0;
|
| 111 |
+
display: flex;
|
| 112 |
+
align-items: center;
|
| 113 |
+
justify-content: space-between;
|
| 114 |
+
}
|
| 115 |
+
</style>
|
| 116 |
+
""", unsafe_allow_html=True)
|
| 117 |
|
| 118 |
st.markdown("""
|
| 119 |
<style>
|
|
|
|
| 640 |
|
| 641 |
# Show profile and sign out in sidebar
|
| 642 |
with st.sidebar:
|
| 643 |
+
# User profile card
|
| 644 |
+
uid = st.session_state.get("uid", "")
|
| 645 |
name = st.session_state.get("user_name", "Student")
|
| 646 |
email = st.session_state.get("user_email", "")
|
| 647 |
avatar = st.session_state.get("user_avatar", "")
|
| 648 |
+
|
| 649 |
+
st.markdown("---")
|
| 650 |
+
col1, col2 = st.columns([1, 3])
|
| 651 |
+
with col1:
|
| 652 |
if avatar:
|
| 653 |
+
st.image(avatar, width=45)
|
| 654 |
+
else:
|
| 655 |
+
# Show colored initial circle
|
| 656 |
+
initial = name[0].upper() if name else "S"
|
| 657 |
+
st.markdown(
|
| 658 |
+
f"""<div style='
|
| 659 |
+
background: linear-gradient(135deg, #7C3AED, #3B82F6);
|
| 660 |
+
color: white;
|
| 661 |
+
border-radius: 50%;
|
| 662 |
+
width: 45px;
|
| 663 |
+
height: 45px;
|
| 664 |
+
display: flex;
|
| 665 |
+
align-items: center;
|
| 666 |
+
justify-content: center;
|
| 667 |
+
font-size: 20px;
|
| 668 |
+
font-weight: bold;
|
| 669 |
+
'>{initial}</div>""",
|
| 670 |
+
unsafe_allow_html=True
|
| 671 |
+
)
|
| 672 |
+
with col2:
|
| 673 |
st.markdown(f"**{name}**")
|
| 674 |
+
st.caption(email[:25] + "..." if len(email) > 25 else email)
|
| 675 |
+
|
| 676 |
+
if st.button("๐ช Sign Out", use_container_width=True):
|
| 677 |
+
for k in ["uid", "user_email", "user_name", "user_avatar",
|
| 678 |
+
"logged_in", "firebase_token", "local_bypass",
|
| 679 |
+
"user_info", "profile_loaded", "study_plan",
|
| 680 |
+
"topic_scores", "mastery_data", "chat_history"]:
|
| 681 |
+
st.session_state.pop(k, None)
|
| 682 |
+
st.rerun()
|
| 683 |
+
|
| 684 |
+
st.markdown("---")
|
| 685 |
|
| 686 |
# Session State
|
| 687 |
if "timer_running" not in st.session_state: st.session_state.timer_running = False
|
|
|
|
| 1176 |
if not st.session_state.focus_mode:
|
| 1177 |
with left_col:
|
| 1178 |
# ====== USER INFO & SIGN-OUT REMOVED (now in sidebar) ======
|
| 1179 |
+
st.markdown("""
|
| 1180 |
+
<h2 style='
|
| 1181 |
+
color:#E2E8F0;
|
| 1182 |
+
font-size:18px;
|
| 1183 |
+
font-weight:700;
|
| 1184 |
+
margin-bottom:16px;
|
| 1185 |
+
padding-bottom:8px;
|
| 1186 |
+
border-bottom:2px solid #7C3AED;
|
| 1187 |
+
'>๐ฎ Control Center</h2>
|
| 1188 |
+
""", unsafe_allow_html=True)
|
| 1189 |
|
| 1190 |
# Timer Widget
|
| 1191 |
with st.container(border=True):
|
| 1192 |
+
st.markdown("""
|
| 1193 |
+
<div style='
|
| 1194 |
+
background: #1E1E30;
|
| 1195 |
+
border: 1px solid #374151;
|
| 1196 |
+
border-radius: 12px;
|
| 1197 |
+
padding: 20px;
|
| 1198 |
+
text-align: center;
|
| 1199 |
+
margin-bottom: 16px;
|
| 1200 |
+
'>
|
| 1201 |
+
<p style='color:#94A3B8;margin:0;font-size:13px'>
|
| 1202 |
+
โฑ๏ธ Study Timer
|
| 1203 |
+
</p>
|
| 1204 |
+
</div>
|
| 1205 |
+
""", unsafe_allow_html=True)
|
| 1206 |
|
| 1207 |
# Timer Logic
|
| 1208 |
total_seconds = (st.session_state.time_left_m * 60) + st.session_state.time_left_s
|
|
|
|
| 1343 |
with st.container(border=True):
|
| 1344 |
# Connectivity Check
|
| 1345 |
is_online = check_internet()
|
| 1346 |
+
status_color = "online-badge" if is_online else "status-badge offline"
|
| 1347 |
status_text = "ONLINE" if is_online else "OFFLINE"
|
| 1348 |
|
| 1349 |
st.markdown(f"""
|
| 1350 |
+
<div style='display:flex;align-items:center;
|
| 1351 |
+
justify-content:space-between;margin-bottom:12px'>
|
| 1352 |
+
<span style='color:#E2E8F0;font-weight:600'>Sources</span>
|
| 1353 |
+
<span class='{status_color}'>{status_text}</span>
|
| 1354 |
+
</div>
|
| 1355 |
""", unsafe_allow_html=True)
|
| 1356 |
|
| 1357 |
# Tabs
|
|
|
|
| 1376 |
|
| 1377 |
c1, c2 = st.columns([0.85, 0.15])
|
| 1378 |
with c1:
|
| 1379 |
+
short = src['filename'][:25] + "..." if len(src['filename']) > 25 else src['filename']
|
| 1380 |
+
st.markdown(
|
| 1381 |
+
f"""<div class='source-card'>
|
| 1382 |
+
<span>{icon} {short}</span>
|
| 1383 |
+
</div>""",
|
| 1384 |
+
unsafe_allow_html=True
|
| 1385 |
+
)
|
| 1386 |
with c2:
|
| 1387 |
if st.button("๐๏ธ", key=f"del_{src['id']}", help="Delete source", type="tertiary"):
|
| 1388 |
try:
|
|
|
|
| 1394 |
st.error(f"Error: {e}")
|
| 1395 |
else:
|
| 1396 |
st.markdown("""
|
| 1397 |
+
<div style='text-align:center;padding:20px;color:#64748B'>
|
| 1398 |
+
<div style='font-size:32px'>๐</div>
|
| 1399 |
+
<p>No sources added yet</p>
|
| 1400 |
</div>
|
| 1401 |
+
""", unsafe_allow_html=True)
|
| 1402 |
|
| 1403 |
# --- Add Source Section ---
|
| 1404 |
st.markdown("<br>", unsafe_allow_html=True)
|
|
|
|
| 1670 |
# Header
|
| 1671 |
h_col1, h_col2 = st.columns([0.8, 0.2])
|
| 1672 |
with h_col1:
|
| 1673 |
+
st.markdown("""
|
| 1674 |
+
<h2 style='
|
| 1675 |
+
color:#E2E8F0;
|
| 1676 |
+
font-size:18px;
|
| 1677 |
+
font-weight:700;
|
| 1678 |
+
margin-bottom:16px;
|
| 1679 |
+
padding-bottom:8px;
|
| 1680 |
+
border-bottom:2px solid #3B82F6;
|
| 1681 |
+
'>๐ง Intelligent Workspace</h2>
|
| 1682 |
+
""", unsafe_allow_html=True)
|
| 1683 |
with h_col2:
|
| 1684 |
if st.button("๐ Analytics"):
|
| 1685 |
show_analytics_dialog()
|
|
|
|
| 1695 |
with chat_container:
|
| 1696 |
if not st.session_state.chat_history:
|
| 1697 |
# Welcome Content
|
|
|
|
| 1698 |
st.markdown("""
|
| 1699 |
+
<div style='
|
| 1700 |
+
background: linear-gradient(135deg, #1E1E30, #2D1B69);
|
| 1701 |
+
border: 1px solid #7C3AED44;
|
| 1702 |
+
border-radius: 16px;
|
| 1703 |
+
padding: 40px;
|
| 1704 |
+
text-align: center;
|
| 1705 |
+
margin: 20px 0;
|
| 1706 |
+
'>
|
| 1707 |
+
<div style='font-size:48px;margin-bottom:16px'>๐</div>
|
| 1708 |
+
<h2 style='color:#E2E8F0;margin:0 0 8px'>
|
| 1709 |
+
Welcome to FocusFlow
|
| 1710 |
+
</h2>
|
| 1711 |
+
<p style='color:#94A3B8;margin:0 0 24px'>
|
| 1712 |
+
Your AI-powered study companion
|
| 1713 |
+
</p>
|
| 1714 |
+
<div style='
|
| 1715 |
+
display:flex;
|
| 1716 |
+
justify-content:center;
|
| 1717 |
+
gap:24px;
|
| 1718 |
+
flex-wrap:wrap;
|
| 1719 |
+
'>
|
| 1720 |
+
<div style='color:#94A3B8;font-size:14px'>
|
| 1721 |
+
๐ Upload a PDF
|
| 1722 |
+
</div>
|
| 1723 |
+
<div style='color:#94A3B8;font-size:14px'>
|
| 1724 |
+
๐ Paste your notes
|
| 1725 |
+
</div>
|
| 1726 |
+
<div style='color:#94A3B8;font-size:14px'>
|
| 1727 |
+
๐๏ธ Generate a study plan
|
| 1728 |
+
</div>
|
| 1729 |
+
</div>
|
| 1730 |
</div>
|
| 1731 |
""", unsafe_allow_html=True)
|
| 1732 |
else:
|
|
|
|
| 1799 |
if right_col:
|
| 1800 |
with right_col:
|
| 1801 |
# --- CALENDAR WIDGET ---
|
| 1802 |
+
st.markdown("""
|
| 1803 |
+
<h2 style='
|
| 1804 |
+
color:#E2E8F0;
|
| 1805 |
+
font-size:18px;
|
| 1806 |
+
font-weight:700;
|
| 1807 |
+
margin-bottom:16px;
|
| 1808 |
+
padding-bottom:8px;
|
| 1809 |
+
border-bottom:2px solid #10B981;
|
| 1810 |
+
'>๐
Study Calendar</h2>
|
| 1811 |
+
""", unsafe_allow_html=True)
|
| 1812 |
today = date.today()
|
| 1813 |
+
selected_date = st.date_input("Select Date", value=today)
|
| 1814 |
|
| 1815 |
# --- LOGIC: Show plan for selected date ---
|
| 1816 |
# If user selects a future date, show its plan inline
|