""" ui.py — Expert-level dark design system with fixed calendar + vivid components. """ BASE_CSS = """ """ def inject_css(): import streamlit as st st.markdown(BASE_CSS, unsafe_allow_html=True) # ────────────────────────────────────────────────────────────────────────────── # COMPONENTS # ────────────────────────────────────────────────────────────────────────────── def stat_card(icon, label, value, sub="", accent="#7c7cf7", glow=True): r, g, b = _hex_to_rgb_tuple(accent) glow_css = f"box-shadow:0 0 28px rgba({r},{g},{b},0.22);" if glow else "" return f"""
{icon}
{value}
{label}
{f'
{sub}
' if sub else ''}
""" def section_header(title, sub="", icon=""): return f"""
{icon+' ' if icon else ''}{title}
{f'
{sub}
' if sub else ''}
""" def badge(text, color="#7c7cf7"): return (f'{text}') def exercise_card(icon, name, sets, reps, rest, idx=0, desc=""): desc_html = (f'
{desc}
') if desc else "" sets_reps = (f'{sets}x{reps}') if sets else "" rest_badge = (f'{rest}') if rest and rest != "." else "" return f"""
{icon}
{name}
{desc_html}
{sets_reps}{rest_badge}
""" def progress_ring(pct, size=80, stroke=6, color="#7c7cf7"): r = (size - stroke) // 2 circ = 2 * 3.14159 * r offset = circ * (1 - pct / 100) return f""" {pct}% """ def calendar_widget(tracking, num_days, today_str): """ Monthly calendar view showing full current month. Each cell displays the date number clearly, colored by workout status. """ from datetime import date, timedelta import calendar as cal_mod today = date.today() year = today.year month = today.month month_name = today.strftime("%B %Y") days_in_month = cal_mod.monthrange(year, month)[1] first_weekday = date(year, month, 1).weekday() # 0=Mon … 6=Sun # ── Day-of-week header ──────────────────────────────────────────────────── day_hdrs = "".join( f'
{d}
' for d in ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"] ) # ── Blank leading cells ─────────────────────────────────────────────────── cells = "".join('
' for _ in range(first_weekday)) # ── Day cells ───────────────────────────────────────────────────────────── for day_num in range(1, days_in_month + 1): d = date(year, month, day_num) ds = d.isoformat() s = tracking.get(ds, {}).get("status", "none") is_today = (d == today) is_future = (d > today) # colors if is_future: bg = "rgba(255,255,255,0.03)" num_col = "rgba(238,238,255,0.18)" bdr = "rgba(255,255,255,0.05)" elif s == "done": bg = "#3d3d9e" num_col = "#ffffff" bdr = "#5b5bd6" elif s == "skipped": bg = "rgba(245,158,11,0.22)" num_col = "#f5c842" bdr = "rgba(245,158,11,0.50)" elif d < today: # missed bg = "rgba(244,63,94,0.10)" num_col = "rgba(244,63,94,0.55)" bdr = "rgba(244,63,94,0.22)" else: # today, pending bg = "rgba(124,124,247,0.20)" num_col = "#c4b5fd" bdr = "rgba(124,124,247,0.60)" today_ring = ( "box-shadow:0 0 0 2px #7c7cf7,0 0 14px rgba(124,124,247,0.40);" ) if is_today else "" # small status dot below number dot = "" if s == "done": dot = '
' elif s == "skipped": dot = '
' cells += f"""
{day_num} {dot}
""" # ── Legend + container ──────────────────────────────────────────────────── return f"""
{month_name}
Done Skipped Missed
{day_hdrs}
{cells}
""" # ── internal helpers ────────────────────────────────────────────────────────── def _hex_to_rgb_tuple(hex_color): h = hex_color.lstrip('#') try: return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16) except Exception: return 124, 124, 247 def _hex_to_rgb(hex_color): r, g, b = _hex_to_rgb_tuple(hex_color) return f"{r},{g},{b}"