Spaces:
Sleeping
Sleeping
Download components.py from Mohammedallyl/strem: direct link, hf CLI and curl.
- Browser
- Download file 18.2 kB
-
https://huggingface.co/spaces/Mohammedallyl/strem/resolve/main/components.py
- Command line
-
hf download hf://spaces/Mohammedallyl/strem/components.py
-
curl -L -o components.py https://huggingface.co/spaces/Mohammedallyl/strem/resolve/main/components.py
18.2 kB
| import streamlit as st | |
| CHART_HEIGHT = 520 | |
| # ββ DESIGN SYSTEM TOKENS β aligned with tijartek_dashboard_html.html ββ | |
| COLORS = { | |
| # Core palette (matches HTML :root vars) | |
| "primary": "#00c9b1", # --teal | |
| "primary_dark": "#009985", | |
| "accent": "#0085ff", # --blue | |
| "accent2": "#ff4d6d", # --red | |
| "accent3": "#f5c842", # --gold | |
| "accent4": "#9b6dff", # --purple | |
| "success": "#00e096", # --green | |
| "warning": "#f5c842", # --gold | |
| "danger": "#ff4d6d", # --red | |
| # Backgrounds (matches HTML) | |
| "bg_dark": "#030d1a", # --bg | |
| "bg_card": "#0d2540", # --card | |
| "bg_card2": "#0f2d4a", # --card2 | |
| "bg2": "#061526", # --bg2 | |
| "bg3": "#0a1f35", # --bg3 | |
| # Text | |
| "text_primary": "#e8f4ff", # --text | |
| "text_muted": "#7aadcc", # --text2 | |
| "text_dim": "#3d6880", # --text3 | |
| # Border | |
| "border": "rgba(0,201,177,0.12)", # --border | |
| } | |
| PLOTLY_PALETTE = [ | |
| "#00c9b1", # teal | |
| "#0085ff", # blue | |
| "#f5c842", # gold | |
| "#ff4d6d", # red | |
| "#00e096", # green | |
| "#9b6dff", # purple | |
| "#38a8ff", # blue2 | |
| "#00e5cc", # teal2 | |
| ] | |
| CHART_LAYOUT = dict( | |
| paper_bgcolor="rgba(0,0,0,0)", | |
| plot_bgcolor="rgba(0,0,0,0)", | |
| font=dict(family="'DM Sans', sans-serif", color="#7aadcc", size=12), | |
| title_font=dict(family="'Syne', sans-serif", color="#e8f4ff", size=15), | |
| legend=dict( | |
| bgcolor="rgba(13,37,64,0.92)", | |
| bordercolor="rgba(0,201,177,0.18)", | |
| borderwidth=1, | |
| font=dict(color="#7aadcc", size=11), | |
| ), | |
| xaxis=dict( | |
| gridcolor="rgba(0,201,177,0.06)", | |
| linecolor="rgba(0,201,177,0.12)", | |
| tickfont=dict(color="#3d6880"), | |
| ), | |
| yaxis=dict( | |
| gridcolor="rgba(0,201,177,0.06)", | |
| linecolor="rgba(0,201,177,0.12)", | |
| tickfont=dict(color="#3d6880"), | |
| ), | |
| margin=dict(l=16, r=16, t=56, b=16), | |
| hoverlabel=dict( | |
| bgcolor="#0d2540", | |
| bordercolor="rgba(0,201,177,0.35)", | |
| font=dict(color="#e8f4ff", size=12), | |
| ), | |
| ) | |
| GLOBAL_CSS = """ | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:wght@300;400;500;600&family=DM+Mono:wght@400;500&display=swap'); | |
| /* ββ CSS VARIABLES β mirrors tijartek_dashboard_html.html ββ */ | |
| :root { | |
| --bg: #030d1a; | |
| --bg2: #061526; | |
| --bg3: #0a1f35; | |
| --card: #0d2540; | |
| --card2: #0f2d4a; | |
| --teal: #00c9b1; | |
| --teal2: #00e5cc; | |
| --blue: #0085ff; | |
| --blue2: #38a8ff; | |
| --gold: #f5c842; | |
| --red: #ff4d6d; | |
| --green: #00e096; | |
| --purple: #9b6dff; | |
| --text: #e8f4ff; | |
| --text2: #7aadcc; | |
| --text3: #3d6880; | |
| --border: rgba(0,201,177,0.12); | |
| --glow: 0 0 40px rgba(0,201,177,0.15); | |
| --glow2: 0 0 20px rgba(0,133,255,0.2); | |
| } | |
| /* ββ BASE RESET ββ */ | |
| html, body, [class*="css"] { | |
| font-family: 'DM Sans', sans-serif; | |
| } | |
| /* ββ APP BACKGROUND β deep navy with subtle grid overlay ββ */ | |
| .stApp { | |
| background: var(--bg); | |
| background-image: | |
| linear-gradient(rgba(0,201,177,0.025) 1px, transparent 1px), | |
| linear-gradient(90deg, rgba(0,201,177,0.025) 1px, transparent 1px), | |
| radial-gradient(ellipse 70% 50% at 15% 5%, rgba(0,133,255,0.07) 0%, transparent 55%), | |
| radial-gradient(ellipse 50% 40% at 85% 95%, rgba(0,201,177,0.05) 0%, transparent 55%); | |
| background-size: 40px 40px, 40px 40px, 100% 100%, 100% 100%; | |
| } | |
| /* ββ SIDEBAR ββ */ | |
| section[data-testid="stSidebar"] { | |
| background: var(--bg2) !important; | |
| border-right: 1px solid var(--border) !important; | |
| } | |
| section[data-testid="stSidebar"] * { color: var(--text2) !important; } | |
| section[data-testid="stSidebar"] h1, | |
| section[data-testid="stSidebar"] h2, | |
| section[data-testid="stSidebar"] h3 { color: var(--text) !important; } | |
| /* ββ METRIC CARDS β match .kpi cards in HTML ββ */ | |
| [data-testid="metric-container"] { | |
| background: var(--card) !important; | |
| border: 1px solid var(--border) !important; | |
| border-radius: 12px !important; | |
| padding: 20px !important; | |
| position: relative; | |
| overflow: hidden; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| } | |
| [data-testid="metric-container"]:hover { | |
| transform: translateY(-2px); | |
| box-shadow: var(--glow) !important; | |
| } | |
| [data-testid="metric-container"]::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: 0; right: 0; | |
| height: 2px; | |
| background: linear-gradient(90deg, var(--teal), transparent); | |
| border-radius: 12px 12px 0 0; | |
| } | |
| [data-testid="stMetricLabel"] { | |
| color: var(--text3) !important; | |
| font-size: 0.68rem !important; | |
| font-weight: 500 !important; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| } | |
| [data-testid="stMetricValue"] { | |
| color: var(--teal) !important; | |
| font-family: 'Syne', sans-serif !important; | |
| font-size: 1.75rem !important; | |
| font-weight: 700 !important; | |
| line-height: 1; | |
| } | |
| [data-testid="stMetricDelta"] { | |
| font-size: 0.78rem !important; | |
| color: var(--text2) !important; | |
| } | |
| /* ββ KPI COLOR VARIANTS β accent top bars ββ */ | |
| [data-testid="metric-container"].kpi-blue::before { background: linear-gradient(90deg, var(--blue), transparent); } | |
| [data-testid="metric-container"].kpi-blue [data-testid="stMetricValue"] { color: var(--blue2) !important; } | |
| [data-testid="metric-container"].kpi-gold::before { background: linear-gradient(90deg, var(--gold), transparent); } | |
| [data-testid="metric-container"].kpi-gold [data-testid="stMetricValue"] { color: var(--gold) !important; } | |
| [data-testid="metric-container"].kpi-red::before { background: linear-gradient(90deg, var(--red), transparent); } | |
| [data-testid="metric-container"].kpi-red [data-testid="stMetricValue"] { color: var(--red) !important; } | |
| [data-testid="metric-container"].kpi-green::before { background: linear-gradient(90deg, var(--green), transparent); } | |
| [data-testid="metric-container"].kpi-green [data-testid="stMetricValue"]{ color: var(--green) !important; } | |
| [data-testid="metric-container"].kpi-purple::before { background: linear-gradient(90deg, var(--purple), transparent); } | |
| [data-testid="metric-container"].kpi-purple [data-testid="stMetricValue"]{ color: var(--purple) !important; } | |
| /* ββ PLOTLY CHARTS ββ */ | |
| .js-plotly-plot .plotly { border-radius: 10px; } | |
| /* ββ SECTION HEADERS β .section-title / .page-title from HTML ββ */ | |
| .tj-section-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| margin: 8px 0 4px 0; | |
| } | |
| .tj-section-header h2 { | |
| font-family: 'Syne', sans-serif; | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| color: var(--text); | |
| margin: 0; | |
| letter-spacing: -0.02em; | |
| } | |
| /* ββ CARD CONTAINERS β match .card in HTML ββ */ | |
| .tj-card { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 14px; | |
| padding: 22px; | |
| position: relative; | |
| overflow: hidden; | |
| margin-bottom: 16px; | |
| } | |
| .tj-card-title { | |
| font-family: 'Syne', sans-serif; | |
| font-weight: 600; | |
| font-size: 13px; | |
| color: var(--text); | |
| margin-bottom: 4px; | |
| } | |
| .tj-card-sub { | |
| font-size: 11px; | |
| color: var(--text3); | |
| margin-bottom: 16px; | |
| } | |
| /* ββ FILTER BAR ββ */ | |
| .tj-filter-bar { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 14px 20px; | |
| margin-bottom: 20px; | |
| } | |
| /* ββ INSIGHT BADGE β match .section-tag in HTML ββ */ | |
| .tj-badge { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| background: rgba(0,201,177,0.08); | |
| border: 1px solid rgba(0,201,177,0.2); | |
| color: var(--teal); | |
| font-size: 0.68rem; | |
| font-weight: 600; | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| padding: 3px 10px; | |
| border-radius: 20px; | |
| margin-bottom: 6px; | |
| } | |
| /* ββ PROGRESS BAR β match .progress-bar in HTML ββ */ | |
| .tj-progress-wrap { margin-bottom: 10px; } | |
| .tj-progress-label { | |
| display: flex; | |
| justify-content: space-between; | |
| font-size: 12px; | |
| color: var(--text2); | |
| margin-bottom: 5px; | |
| } | |
| .tj-progress-bar { | |
| height: 4px; | |
| background: var(--bg3); | |
| border-radius: 2px; | |
| overflow: hidden; | |
| } | |
| .tj-progress-fill { | |
| height: 100%; | |
| border-radius: 2px; | |
| background: linear-gradient(90deg, var(--teal), var(--blue2)); | |
| } | |
| .tj-progress-fill.gold { background: linear-gradient(90deg, var(--gold), #ff9f1c); } | |
| .tj-progress-fill.red { background: linear-gradient(90deg, var(--red), #ff8c69); } | |
| .tj-progress-fill.green { background: linear-gradient(90deg, var(--green), #00b4d8); } | |
| .tj-progress-fill.purple { background: linear-gradient(90deg, var(--purple), var(--blue2)); } | |
| /* ββ ALERT ITEMS β match .alert-item in HTML ββ */ | |
| .tj-alert { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 11px 14px; | |
| border-radius: 8px; | |
| background: var(--bg3); | |
| border-left: 3px solid var(--red); | |
| font-size: 12px; | |
| color: var(--text2); | |
| margin-bottom: 8px; | |
| } | |
| .tj-alert.warn { border-left-color: var(--gold); } | |
| .tj-alert.info { border-left-color: var(--blue); } | |
| .tj-alert.ok { border-left-color: var(--green); } | |
| /* ββ DATA TABLE β match .data-table in HTML ββ */ | |
| .tj-table { width: 100%; border-collapse: collapse; } | |
| .tj-table th { | |
| font-size: 10px; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| color: var(--text3); | |
| padding: 8px 12px; | |
| text-align: left; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .tj-table td { | |
| font-size: 12px; | |
| color: var(--text2); | |
| padding: 10px 12px; | |
| border-bottom: 1px solid rgba(0,201,177,0.04); | |
| } | |
| .tj-table tr:hover td { background: rgba(0,201,177,0.03); color: var(--text); } | |
| /* ββ RANK BADGES ββ */ | |
| .tj-rank { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 20px; height: 20px; | |
| border-radius: 50%; | |
| font-size: 10px; | |
| font-weight: 700; | |
| background: var(--card2); | |
| color: var(--text3); | |
| } | |
| .tj-rank.gold { background: rgba(245,200,66,0.18); color: var(--gold); } | |
| .tj-rank.silver { background: rgba(122,173,204,0.15); color: var(--text2); } | |
| .tj-rank.bronze { background: rgba(200,120,60,0.15); color: #c8783c; } | |
| /* ββ LIVE BADGE ββ */ | |
| .tj-live { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| font-size: 11px; | |
| color: var(--green); | |
| font-weight: 500; | |
| } | |
| .tj-live-dot { | |
| width: 6px; height: 6px; | |
| border-radius: 50%; | |
| background: var(--green); | |
| animation: tjPulse 2s infinite; | |
| } | |
| @keyframes tjPulse { | |
| 0%,100% { opacity:1; transform:scale(1); } | |
| 50% { opacity:0.5; transform:scale(1.4); } | |
| } | |
| /* ββ DIVIDER ββ */ | |
| hr { border-color: rgba(0,201,177,0.08) !important; } | |
| /* ββ INPUTS ββ */ | |
| .stTextInput input, .stNumberInput input { | |
| background: var(--card2) !important; | |
| border-color: var(--border) !important; | |
| color: var(--text) !important; | |
| border-radius: 8px !important; | |
| } | |
| .stSelectbox > div > div { | |
| background: var(--card2) !important; | |
| border-color: var(--border) !important; | |
| color: var(--text) !important; | |
| border-radius: 8px !important; | |
| } | |
| /* ββ BUTTONS ββ */ | |
| .stButton > button { | |
| background: linear-gradient(135deg, var(--teal), var(--blue)) !important; | |
| color: #030d1a !important; | |
| border: none !important; | |
| border-radius: 8px !important; | |
| font-weight: 700 !important; | |
| font-family: 'DM Sans', sans-serif !important; | |
| letter-spacing: 0.02em; | |
| transition: opacity 0.2s, transform 0.15s !important; | |
| } | |
| .stButton > button:hover { | |
| opacity: 0.88 !important; | |
| transform: translateY(-1px) !important; | |
| } | |
| /* ββ EXPANDER ββ */ | |
| .streamlit-expanderHeader { | |
| background: var(--card) !important; | |
| border: 1px solid var(--border) !important; | |
| border-radius: 10px !important; | |
| color: var(--text2) !important; | |
| } | |
| /* ββ TABS ββ */ | |
| [data-testid="stTabs"] [role="tablist"] { | |
| background: var(--card) !important; | |
| border-radius: 10px !important; | |
| border: 1px solid var(--border) !important; | |
| padding: 4px; | |
| } | |
| [data-testid="stTabs"] [role="tab"] { | |
| color: var(--text3) !important; | |
| font-family: 'DM Sans', sans-serif !important; | |
| border-radius: 7px !important; | |
| } | |
| [data-testid="stTabs"] [role="tab"][aria-selected="true"] { | |
| background: rgba(0,201,177,0.12) !important; | |
| color: var(--teal) !important; | |
| } | |
| /* ββ ALERT / INFO BOXES ββ */ | |
| .stAlert { | |
| border-radius: 10px !important; | |
| background: var(--card) !important; | |
| border-color: var(--border) !important; | |
| } | |
| /* ββ CHAT MESSAGES ββ */ | |
| [data-testid="stChatMessage"] { | |
| background: var(--card) !important; | |
| border: 1px solid var(--border) !important; | |
| border-radius: 12px !important; | |
| } | |
| /* ββ DATAFRAME / TABLE ββ */ | |
| [data-testid="stDataFrame"] { | |
| border: 1px solid var(--border) !important; | |
| border-radius: 10px !important; | |
| } | |
| /* ββ SCROLLBAR ββ */ | |
| ::-webkit-scrollbar { width: 4px; } | |
| ::-webkit-scrollbar-track { background: var(--bg2); } | |
| ::-webkit-scrollbar-thumb { background: var(--text3); border-radius: 2px; } | |
| /* ββ SPINNER / STATUS ββ */ | |
| .stSpinner > div { border-top-color: var(--teal) !important; } | |
| /* ββ PAGE LAYOUT HELPERS ββ */ | |
| .block-container { | |
| padding-top: 1.5rem !important; | |
| padding-bottom: 2rem !important; | |
| } | |
| </style> | |
| """ | |
| def inject_theme(): | |
| """Inject the global CSS design system (aligned with HTML dashboard).""" | |
| st.markdown(GLOBAL_CSS, unsafe_allow_html=True) | |
| def section_title(title: str, subtitle: str = ""): | |
| """Styled section header matching HTML .page-title / .section-title style.""" | |
| html = f""" | |
| <div class="tj-section-header"> | |
| <h2>{title}</h2> | |
| </div>""" | |
| if subtitle: | |
| html += f'<p style="color:#3d6880;font-size:0.83rem;margin:0 0 14px 0;letter-spacing:0.01em;">{subtitle}</p>' | |
| st.markdown(html, unsafe_allow_html=True) | |
| def render_kpi_row(metrics: list, num_cols: int = 4): | |
| """ | |
| Render a row of KPI metric cards. | |
| Each metric dict may include: label, value, delta, color (teal/blue/gold/red/green/purple). | |
| The color controls the top accent bar and value color via CSS class. | |
| """ | |
| cols = st.columns(num_cols) | |
| for i, m in enumerate(metrics): | |
| with cols[i % num_cols]: | |
| color_cls = m.get("color", "") | |
| # Inject a wrapper div with the appropriate kpi color class | |
| if color_cls and color_cls != "teal": | |
| st.markdown( | |
| f'<style>[data-testid="metric-container"]:nth-child({i+1}) ' | |
| f'{{ border-top: 2px solid var(--{color_cls}) !important; }}</style>', | |
| unsafe_allow_html=True, | |
| ) | |
| st.metric( | |
| label=m.get("label", "β"), | |
| value=m.get("value", "β"), | |
| delta=m.get("delta"), | |
| ) | |
| st.markdown("<div style='margin-bottom:4px'></div>", unsafe_allow_html=True) | |
| def filter_bar(label: str = "Filters"): | |
| """Styled container for filter rows.""" | |
| return st.container() | |
| def insight_badge(text: str): | |
| """Render a small teal pill badge β matches .section-tag in HTML.""" | |
| st.markdown(f'<span class="tj-badge">{text}</span>', unsafe_allow_html=True) | |
| def empty_state(message: str, icon: str = "π"): | |
| """Placeholder card for missing data.""" | |
| st.markdown( | |
| f""" | |
| <div style='text-align:center;padding:40px 20px;background:var(--card); | |
| border:1px dashed rgba(0,201,177,0.15);border-radius:14px;margin:8px 0;'> | |
| <div style='font-size:2rem;margin-bottom:10px;opacity:0.5'>{icon}</div> | |
| <p style='color:#3d6880;font-size:0.88rem;margin:0'>{message}</p> | |
| </div> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| def progress_row(label: str, value_label: str, pct: float, color: str = ""): | |
| """ | |
| Render a labeled progress bar row β matches HTML progress-bar pattern. | |
| color: '', 'gold', 'red', 'green', 'purple' | |
| pct: 0β100 | |
| """ | |
| fill_class = f"tj-progress-fill {color}".strip() | |
| st.markdown( | |
| f""" | |
| <div class="tj-progress-wrap"> | |
| <div class="tj-progress-label"> | |
| <span>{label}</span> | |
| <span style="color:var(--teal);font-weight:600">{value_label}</span> | |
| </div> | |
| <div class="tj-progress-bar"> | |
| <div class="{fill_class}" style="width:{min(max(pct,0),100):.1f}%"></div> | |
| </div> | |
| </div> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| def alert_row(message: str, level: str = "error"): | |
| """ | |
| Render a styled alert item β matches HTML .alert-item. | |
| level: 'error' (red), 'warn' (gold), 'info' (blue), 'ok' (green) | |
| """ | |
| cls_map = {"error": "", "warn": "warn", "info": "info", "ok": "ok"} | |
| icon_map = {"error": "β οΈ", "warn": "β‘", "info": "βΉοΈ", "ok": "β "} | |
| cls = cls_map.get(level, "") | |
| icon = icon_map.get(level, "β’") | |
| st.markdown( | |
| f'<div class="tj-alert {cls}">' | |
| f'<span style="font-size:14px">{icon}</span>' | |
| f'<span>{message}</span>' | |
| f'</div>', | |
| unsafe_allow_html=True, | |
| ) | |
| def rank_table_html(rows: list, headers: list) -> str: | |
| """ | |
| Build an HTML rank table string (use with st.markdown unsafe_allow_html=True). | |
| rows: list of lists; first column treated as rank number (1-3 get gold/silver/bronze badges). | |
| headers: list of column header strings. | |
| """ | |
| th = "".join(f"<th>{h}</th>" for h in headers) | |
| tbody = "" | |
| for i, row in enumerate(rows): | |
| tds = "" | |
| for j, cell in enumerate(row): | |
| if j == 0: | |
| if i == 0: badge_cls = "gold" | |
| elif i == 1: badge_cls = "silver" | |
| elif i == 2: badge_cls = "bronze" | |
| else: badge_cls = "" | |
| tds += f'<td><span class="tj-rank {badge_cls}">{cell}</span></td>' | |
| else: | |
| tds += f"<td>{cell}</td>" | |
| tbody += f"<tr>{tds}</tr>" | |
| return f'<table class="tj-table"><thead><tr>{th}</tr></thead><tbody>{tbody}</tbody></table>' |