ST_AI / app.py
UCS2014's picture
Update app.py
8b91916 verified
raw
history blame
7.64 kB
# -*- coding: utf-8 -*-
import base64, mimetypes
from html import escape
from pathlib import Path
import streamlit as st
# ========= PATHS =========
BASE_DIR = Path(__file__).parent
ASSETS = BASE_DIR / "assets"
# ========= META =========
st.set_page_config(page_title="ST_LOG SUITE — Apps", page_icon="🧭", layout="wide")
# ========= BRAND / DATA =========
SUITE_NAME = "ST_LOG SUITE"
ACCENT = "#0E7490" # teal start (kept subtly in chips)
ACCENT_END = "#14B8A6" # teal end
# Logos (put these PNGs in assets/)
HERO_LOGO = ASSETS / "AI_Suite_Log_logo.png"
ICON_GR = ASSETS / "GR_logo.png"
ICON_TS = ASSETS / "Ts_logo.png"
ICON_TC = ASSETS / "Tc_logo.png"
APPS = [
{
"title": "ST_Log_GR",
"url": "https://smart-thinking-gr.hf.space/",
"blurb": "Real-time gamma-ray log prediction.",
"icon": ICON_GR,
},
{
"title": "ST_Log_Sonic (Ts)",
"url": "https://smart-thinking-sonic-ts.hf.space",
"blurb": "Predict shear slowness (DtS) in real time.",
"icon": ICON_TS,
},
{
"title": "ST_Log_Sonic (Tc)",
"url": "https://smart-thinking-sonic-tc.hf.space",
"blurb": "Predict compressional slowness (DtC) in real time.",
"icon": ICON_TC,
},
]
# ========= HELPERS =========
def data_uri(path: Path) -> str | None:
if not path or not path.exists():
return None
mime, _ = mimetypes.guess_type(path.name)
if not mime:
mime = "image/png"
b64 = base64.b64encode(path.read_bytes()).decode("utf-8")
return f"data:{mime};base64,{b64}"
def img_tag(path: Path, alt: str, cls: str = "", style: str = "") -> str:
uri = data_uri(path)
if not uri:
return ""
cls_attr = f' class="{cls}"' if cls else ""
style_attr = f' style="{style}"' if style else ""
return f'<img{cls_attr}{style_attr} src="{uri}" alt="{escape(alt)}" />'
# ========= BACKGROUND (clean, calm gradient) =========
st.markdown(f"""
<style>
:root {{
--brand: {ACCENT};
--brand2: {ACCENT_END};
--btnDark1: #0B1220; /* dark slate for button */
--btnDark2: #334155; /* slate */
}}
html, body, [data-testid="stAppViewContainer"] {{ height: 100%; }}
[data-testid="stAppViewContainer"] > .main {{
padding-top: 0 !important; padding-bottom: 0 !important;
}}
.block-container {{
max-width: 1100px;
min-height: 100vh;
display: flex; flex-direction: column;
gap: 16px;
padding: 72px 0 28px !important;
background:
radial-gradient(980px 460px at 50% -140px,
rgba(20,184,166,0.08) 0%,
rgba(14,116,144,0.05) 26%,
rgba(255,255,255,0.98) 60%,
rgba(255,255,255,1) 100%);
}}
/* HERO (logo only — title/tagline removed) */
.hero {{ text-align:center; margin: 0; }}
.hero img {{
width: 560px; max-width: 92vw; height: auto;
display:block; margin: 0 auto 6px;
filter: drop-shadow(0 6px 16px rgba(0,0,0,.10));
}}
/* SUITE BANNER (tiny, elegant) */
.suite-banner {{
display:flex; gap:10px; justify-content:center; align-items:center;
margin: 4px 0 10px;
}}
.suite-pill {{
background: linear-gradient(90deg, var(--brand) 0%, var(--brand2) 100%);
color:#fff; padding:6px 12px; border-radius:999px;
font-weight:700; letter-spacing:.2px;
box-shadow: 0 6px 14px rgba(14,116,144,.20);
}}
/* GRID (3 → 2 → 1) */
.grid {{
display: grid;
grid-template-columns: repeat(3, 320px);
gap: 36px;
justify-content: center;
align-items: stretch;
margin-top: 8px;
}}
@media (max-width: 1100px) {{
.grid {{ grid-template-columns: repeat(2, 320px); gap: 28px; }}
}}
@media (max-width: 700px) {{
.grid {{ grid-template-columns: 1fr; }}
}}
/* CARD — premium look */
.card {{
position: relative;
width: 320px;
background: linear-gradient(180deg, #ffffff 0%, #FBFCFE 100%);
border: 1px solid rgba(12,18,32,.08);
border-radius:20px; padding:22px 18px 18px;
box-shadow: 0 10px 34px rgba(2,20,35,.08), 0 1px 0 rgba(255,255,255,0.9) inset;
transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
text-align:center; display:flex; flex-direction:column; gap:14px;
align-items: center;
}}
.card:hover {{
transform: translateY(-4px);
box-shadow: 0 18px 52px rgba(2,20,35,.16);
border-color: rgba(14,116,144,0.18);
}}
/* Corner suite chip */
.suite-chip {{
position: absolute; top: 12px; right: 12px;
background: linear-gradient(90deg, var(--brand) 0%, var(--brand2) 100%);
color:#fff; padding:6px 10px; border-radius:999px;
font-weight:700; font-size:.82rem; letter-spacing:.2px;
box-shadow: 0 6px 14px rgba(14,116,144,.22);
}}
/* Larger round icon */
.icon-wrap {{
width: 104px; height: 104px; border-radius: 9999px;
background: #F0F5F9;
display:grid; place-items:center;
border: 1px solid rgba(12,18,32,0.10);
box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 8px 20px rgba(2,20,35,.06);
}}
.icon-wrap img {{
width: 92px; height: 92px; border-radius:9999px; display:block;
}}
/* Enlarged app title */
.card h3 {{
margin: 2px 0 0; font-size: 1.26rem; font-weight: 900; color:#0b1220;
letter-spacing: .1px;
}}
.card p {{
color:#566275; min-height: 40px; margin: 0 10px; font-size: .98rem;
}}
/* PREMIUM BUTTON (dark slate, not green) */
.btn {{
display:inline-block; padding:12px 18px; border-radius:14px;
border: 1px solid rgba(15,23,42,.35); text-decoration:none;
background: linear-gradient(180deg, var(--btnDark1) 0%, var(--btnDark2) 100%);
color: white; font-weight: 700; letter-spacing:.25px;
margin: 6px auto 0;
box-shadow: 0 10px 26px rgba(15,23,42,.25), inset 0 1px 0 rgba(255,255,255,.08);
}}
.btn:hover {{
filter: brightness(1.02) saturate(1.02);
transform: translateY(-1px);
box-shadow: 0 14px 34px rgba(15,23,42,.30);
}}
.footer {{ text-align:center; color:#3f4a5a; font-size:0.96em; margin-top: 24px; }}
.footer hr {{ margin: 12px 0; border-color: rgba(0,0,0,.08); }}
</style>
""", unsafe_allow_html=True)
# ========= HERO (logo only) =========
hero_html = img_tag(HERO_LOGO, "ST_LOG SUITE")
st.markdown(f"<div class='hero'>{hero_html}</div>", unsafe_allow_html=True)
# ========= SUITE BANNER =========
st.markdown(f"<div class='suite-banner'><span class='suite-pill'>{escape(SUITE_NAME)}</span></div>", unsafe_allow_html=True)
# ========= CARDS =========
def app_card(app: dict) -> str:
icon_html = img_tag(app.get("icon"), "icon",
style="width:92px;height:92px;border-radius:9999px;") if app.get("icon") and app["icon"].exists() else ""
target = "_self" # change to "_blank" if you prefer new tab
return (
"<div class='card'>"
+ f"<div class='suite-chip'>{escape(SUITE_NAME)}</div>"
+ f"<div class='icon-wrap'>{icon_html}</div>"
+ f"<h3>{escape(app['title'])}</h3>"
+ f"<p>{escape(app['blurb'])}</p>"
+ f"<a class='btn' href='{escape(app['url'])}' target='{target}' rel='noopener'>Run App</a>"
+ "</div>"
)
cards_html = "".join(app_card(a) for a in APPS)
st.markdown(f"<div class='grid'>{cards_html}</div>", unsafe_allow_html=True)
# ========= FOOTER =========
st.markdown(
"""
<hr>
<div class='footer'>
© 2025 Smart Thinking AI-Solutions Team. All rights reserved.<br>
Website: <a href="https://smartthinking.com.sa" target="_blank" rel="noopener noreferrer">smartthinking.com.sa</a>
</div>
""",
unsafe_allow_html=True,
)